[core-rest-pipeline] Preserve header casing#18517
Conversation
Preserve original casing of HTTP header names when converting HttpHeader collections back to JSON and when iterating over stored name/value pairs. Fixes Azure#18367
|
API changes have been detected in |
This looks like a bug - it's pulling changes from core-client... @praveenkuttappan any thoughts? |
| for (const [key, value] of this._headersMap) { | ||
| result[key] = value; | ||
| for (const entry of this._headersMap.values()) { | ||
| result[entry.name] = entry.value; |
There was a problem hiding this comment.
@deyaaeldeen mentioned that this change of behavior could break existing code that assumes lower case key
The LRO engine assumes the retry header has lower case so perhaps we will need to explicitly lower the case there?
yeah I think it's better to fix the incorrect assumption.
There was a problem hiding this comment.
Updating core-lro sounds good to me.
There was a problem hiding this comment.
@deyaaeldeen I looked into this a bit more and would like your feedback.
Since the public surface of core-lro declares that RawResponse.headers are lowercase, perhaps we should preserve this behavior?
The downside is that core-lro doesn't actually produce RawResponse objects, it only consumes them via generated code. So this is tricky to fix. I see some options:
- We change
core-lrowherever it reads headers to look for a lowercased version of each property name (kinda messy) - We update the codegen to lowercase all header names before passing them into
core-lro
For the second one, I could add an optional parameter to toJSON() that lowercases the header names, to make the codegen very similar with toJSON({lowercase: true}) being the only change.
What do you think? Should we change the contract or update codegen?
There was a problem hiding this comment.
@xirzec I think the main trade-off here is that normalization makes it easy to lookup standard headers but makes looking up custom headers inconvenient because these lookups are usually case-sensitive.
I think the best way to handle this is for core to provide two APIs, one for each case. This way, the generated code does not need to be updated.
There was a problem hiding this comment.
@deyaaeldeen that's an interesting idea. Maybe we go the other way and have toJSON({preserveCase: true}) for this? @jeremymeng would that work for Storage?
There was a problem hiding this comment.
@xirzec Storage doesn't use the headers directly. In core if we send the request using the original casing the it should be fine
There was a problem hiding this comment.
OK I ended up splitting the difference. I kept the original casing when iterating over the collection, since then you're not hunting for a particular value (and you can easily toLowercase each thing as you iterate), but I switched toJSON back to lowercasing unless a flag is passed in.
Now xhrHttpClient and nodeHttpClient should both do the right thing, which is send original case for header names. @deyaaeldeen @jeremymeng please take another look when you have time 😄
sdk/identity/identity/src/credentials/managedIdentityCredential/fabricMsi.ts
Show resolved
Hide resolved
This still leaves the iterator with the original casing, so that we don't have to do special handling in xhrHttpClient in order to send the original casing.
jeremymeng
left a comment
There was a problem hiding this comment.
Looks good! Please update CHANGELOG. I am porting this fix to core-http and will open a PR soon.
| if (options.preserveCase) { | ||
| result[entry.name] = entry.value; | ||
| } else { | ||
| result[normalizedName] = entry.value; | ||
| } |
There was a problem hiding this comment.
NIT: perhaps factor out the preserveCase check out the loop so it fires only once?
There was a problem hiding this comment.
Hm yeah I'm not sure if the runtime will optimize this correctly, so I'm going to do as you suggest
|
API changes have been detected in API changes - toJSON(): RawHttpHeaders;
+ toJSON(options?: {
+ preserveCase?: boolean;
+ }): RawHttpHeaders; |
Since identity tests spy on the headers being sent (which have casing preserved now), we do need these changes even though the public interface is maintained.
|
/check-enforcer override |
Preserve original casing of HTTP header names when iterating over stored name/value pairs and optionally when converting HttpHeader collections back to JSON.
Fixes #18367