fix(serve): normalize multiplexed model ID header to support proxy-transformed names#61869
Conversation
There was a problem hiding this comment.
Code Review
The pull request effectively addresses the issue where HTTP proxies transform header names by converting underscores to hyphens. By normalizing the serve_multiplexed_model_id header key to lowercase and replacing hyphens with underscores, the system can now correctly recognize the header regardless of proxy transformations. This ensures that serve.get_multiplexed_model_id() and load_model() function as expected, preventing ValueError due to empty model IDs. The change is well-documented with clear comments explaining the normalization logic.
There was a problem hiding this comment.
Pull request overview
This PR improves Ray Serve’s HTTP proxy request context setup by normalizing the multiplexed model ID header name so requests continue to work when intermediaries (e.g., nginx, AWS API Gateway) transform underscores to hyphens.
Changes:
- Normalize incoming header keys (lowercase +
-→_) before comparing againstSERVE_MULTIPLEXED_MODEL_ID. - Update inline comments to document why this normalization is required for common proxy behavior.
Comments suppressed due to low confidence (1)
python/ray/serve/_private/proxy.py:1243
key.decode()is still being computed multiple times per header (once fornormalized_keyand again for the request-id comparison). Consider decoding/lowercasing once per loop iteration and reusing the decoded value for both comparisons to reduce duplication and keep the matching logic consistent.
normalized_key = key.decode().lower().replace("-", "_")
if normalized_key == SERVE_MULTIPLEXED_MODEL_ID:
multiplexed_model_id = value.decode()
handle = handle.options(multiplexed_model_id=multiplexed_model_id)
request_context_info["multiplexed_model_id"] = multiplexed_model_id
if key.decode() == SERVE_HTTP_REQUEST_ID_HEADER:
request_context_info["request_id"] = value.decode()
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| normalized_key = key.decode().lower().replace("-", "_") | ||
| if normalized_key == SERVE_MULTIPLEXED_MODEL_ID: | ||
| multiplexed_model_id = value.decode() |
themavik
left a comment
There was a problem hiding this comment.
Solid fix for a subtle proxy compatibility issue. The lower().replace("-", "_") normalization elegantly handles nginx, API Gateway, and Cloudflare without changing the public API. One minor thought: have you considered adding a unit test with serve-multiplexed-model-id and Serve-Multiplexed-Model-Id to lock in this behavior?
themavik
left a comment
There was a problem hiding this comment.
Reviewed the changes — the implementation is clean and addresses the reported issue correctly.
themavik
left a comment
There was a problem hiding this comment.
Reviewed the changes — the fix is minimal and targeted. Good contribution.
|
please fix DCO |
…ansformed names HTTP proxies (nginx, AWS API Gateway, etc.) convert underscores to hyphens in header names, so a client sending serve_multiplexed_model_id arrives at Ray Serve as serve-multiplexed-model-id. The previous exact-match comparison silently failed, causing get_multiplexed_model_id() to return '' and load_model() to raise: ValueError: The model ID cannot be empty. Fix: normalize the incoming header key by lowercasing and replacing hyphens with underscores before comparing, so both forms are accepted without any breaking change to existing users or documentation. Signed-off-by: Yunlin Mao <maoyl@smail.nju.edu.cn>
979bd5e to
da03a1a
Compare
|
I have fixed the DCO issue and added unit tests, please review the code. |
|
@Yunnglin there are some lint failures, can you please fix them? |
…tion Add parametrized tests covering all three header forms that should be recognised by setup_request_context_and_handle: - serve_multiplexed_model_id (original underscore form, direct connection) - serve-multiplexed-model-id (hyphen form, produced by nginx / API Gateway) - Serve-Multiplexed-Model-Id (title-case hyphen form, some proxies) Signed-off-by: Yunlin Mao <maoyl@smail.nju.edu.cn>
6f76439 to
4b4d300
Compare
@harshit-anyscale fixed |
|
@harshit-anyscale Can this PR be merged yet? |
|
cc: @abrarsheikh for approval and merge |
|
@abrarsheikh Can this PR be merged yet? |
…ansformed names (ray-project#61869) ## Why are these changes needed? HTTP proxies such as nginx and AWS API Gateway convert underscores to hyphens in header names by default. This means a client sending: ``` serve_multiplexed_model_id: my-model ``` arrives at Ray Serve as: ``` serve-multiplexed-model-id: my-model ``` The previous exact-match comparison in `setup_request_context_and_handle` against the constant `"serve_multiplexed_model_id"` silently failed in this case. As a result: 1. `serve.get_multiplexed_model_id()` returned `""` (empty string) 2. `load_model("")` raised `ValueError: The model ID cannot be empty.` ## What changes were made? In `proxy.py`, normalize the incoming header key before comparing: ```python normalized_key = key.decode().lower().replace("-", "_") if normalized_key == SERVE_MULTIPLEXED_MODEL_ID: ``` This makes both `serve_multiplexed_model_id` and `serve-multiplexed-model-id` (and any case variant like `Serve-Multiplexed-Model-Id`) equivalent — fully backward-compatible with no changes to the constant, docs, or tests. ## Related issues / discussions - Proxies that strip or transform underscores: nginx (`underscores_in_headers off` is the default), AWS API Gateway, Cloudflare, etc. --------- Signed-off-by: Yunlin Mao <maoyl@smail.nju.edu.cn>
…ansformed names (ray-project#61869) ## Why are these changes needed? HTTP proxies such as nginx and AWS API Gateway convert underscores to hyphens in header names by default. This means a client sending: ``` serve_multiplexed_model_id: my-model ``` arrives at Ray Serve as: ``` serve-multiplexed-model-id: my-model ``` The previous exact-match comparison in `setup_request_context_and_handle` against the constant `"serve_multiplexed_model_id"` silently failed in this case. As a result: 1. `serve.get_multiplexed_model_id()` returned `""` (empty string) 2. `load_model("")` raised `ValueError: The model ID cannot be empty.` ## What changes were made? In `proxy.py`, normalize the incoming header key before comparing: ```python normalized_key = key.decode().lower().replace("-", "_") if normalized_key == SERVE_MULTIPLEXED_MODEL_ID: ``` This makes both `serve_multiplexed_model_id` and `serve-multiplexed-model-id` (and any case variant like `Serve-Multiplexed-Model-Id`) equivalent — fully backward-compatible with no changes to the constant, docs, or tests. ## Related issues / discussions - Proxies that strip or transform underscores: nginx (`underscores_in_headers off` is the default), AWS API Gateway, Cloudflare, etc. --------- Signed-off-by: Yunlin Mao <maoyl@smail.nju.edu.cn>
Why are these changes needed?
HTTP proxies such as nginx and AWS API Gateway convert underscores to hyphens in header names by default. This means a client sending:
arrives at Ray Serve as:
The previous exact-match comparison in
setup_request_context_and_handleagainst the constant"serve_multiplexed_model_id"silently failed in this case. As a result:serve.get_multiplexed_model_id()returned""(empty string)load_model("")raisedValueError: The model ID cannot be empty.What changes were made?
In
proxy.py, normalize the incoming header key before comparing:This makes both
serve_multiplexed_model_idandserve-multiplexed-model-id(and any case variant likeServe-Multiplexed-Model-Id) equivalent — fully backward-compatible with no changes to the constant, docs, or tests.Related issues / discussions
underscores_in_headers offis the default), AWS API Gateway, Cloudflare, etc.