Fix proxy_set_header for websocket and custom override locations#53
Fix proxy_set_header for websocket and custom override locations#53mesudip wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a ProxyHeaderProcessor to manage the reinsertion of default proxy headers when custom headers are injected or for websocket locations. It also updates the Jinja2 template to conditionally render these headers and adds comprehensive unit tests. The review feedback highlights a fragile type check in ProxyHeaderProcessor where checking for list instead of str can break other iterable types. Additionally, it suggests simplifying both the Python logic and the Jinja2 template by treating all websocket locations as requiring header reinsertion, which would eliminate redundant hardcoded blocks in the template.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| def _overridden_proxy_header_names(self, injected: Iterable[str] | str | None) -> set[str]: | ||
| if injected is None: | ||
| return set() | ||
| directives = injected if isinstance(injected, list) else [injected] |
There was a problem hiding this comment.
Using isinstance(injected, list) to distinguish between a single string and an iterable of strings is fragile. Since str is also an Iterable, any other iterable type (such as set, tuple, or a custom sequence) will fail the list check and be incorrectly wrapped as a single-element list containing the iterable itself. This causes str(directive) to serialize the entire collection (e.g., "{'proxy_set_header ...'}"), which fails the regex match.
Instead, check if injected is a string to decide whether to wrap it.
| directives = injected if isinstance(injected, list) else [injected] | |
| directives = [injected] if isinstance(injected, str) else injected |
| continue | ||
|
|
||
| overridden_headers = self._overridden_proxy_header_names(location.extras.get("injected", [])) | ||
| needs_reinsertion = bool(overridden_headers) or (location.websocket and not location.http) |
There was a problem hiding this comment.
We can significantly simplify both the Python logic and the Jinja2 template by making needs_reinsertion True for all websocket locations (regardless of whether location.http is True or False).
Since any websocket location block must define at least Upgrade and Connection headers, Nginx's inheritance rules dictate that it will not inherit any global proxy_set_header directives. Therefore, all websocket locations always need the full set of default headers re-inserted.
If we change this condition to:
needs_reinsertion = bool(overridden_headers) or location.websocketThen location.reinsert_global_config will be True for all websocket locations, and location.reinserted_proxy_set_headers will contain the complete list of headers.
This allows us to completely remove the redundant, hardcoded header blocks in vhosts_template/default.conf.jinja2 (lines 57-75), leaving only the clean loop over location.reinserted_proxy_set_headers.
| needs_reinsertion = bool(overridden_headers) or (location.websocket and not location.http) | |
| needs_reinsertion = bool(overridden_headers) or location.websocket |
| proxy_redirect $scheme://$http_host{{ location.container.path if location.container.path else '/' }} $scheme://$http_host{{location.name}};{% endif %} {% if location.reinsert_global_config %}{% for header_name, header_value in location.reinserted_proxy_set_headers %} | ||
| proxy_set_header {{ header_name }} {{ header_value }};{% endfor %}{% elif location.websocket and location.http %} | ||
| proxy_set_header Host $http_host; | ||
| proxy_set_header Connection $connection_upgrade; | ||
| proxy_set_header Upgrade $http_upgrade; | ||
| 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 $proxy_x_forwarded_proto; | ||
| proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl; | ||
| proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;{% elif location.websocket %} | ||
| proxy_http_version 1.1; | ||
| proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port; | ||
| proxy_set_header Proxy "";{% elif location.websocket %} | ||
| proxy_set_header Host $http_host; | ||
| proxy_set_header Connection $connection_upgrade; | ||
| proxy_set_header Upgrade $http_upgrade; | ||
| proxy_set_header Connection "Upgrade"; | ||
| 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 $proxy_x_forwarded_proto; | ||
| proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl; | ||
| proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port; | ||
| proxy_set_header Proxy "";{% endif %}{% if location.websocket and not location.http %} |
There was a problem hiding this comment.
If we simplify the Python logic in ProxyHeaderProcessor to set needs_reinsertion = True for all websocket locations (by changing needs_reinsertion = bool(overridden_headers) or location.websocket), we can completely eliminate the redundant, hardcoded header blocks here.
This leaves only the clean loop over location.reinserted_proxy_set_headers, greatly improving template maintainability.
proxy_redirect $scheme://$http_host{{ location.container.path if location.container.path else '/' }} $scheme://$http_host{{location.name}};{% endif %} {% if location.reinsert_global_config %}{% for header_name, header_value in location.reinserted_proxy_set_headers %}
proxy_set_header {{ header_name }} {{ header_value }};{% endfor %}{% endif %}{% if location.websocket and not location.http %}
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #53 +/- ##
==========================================
+ Coverage 70.31% 70.61% +0.29%
==========================================
Files 31 32 +1
Lines 3261 3301 +40
Branches 542 550 +8
==========================================
+ Hits 2293 2331 +38
- Misses 801 802 +1
- Partials 167 168 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
No description provided.