Skip to content

Conversation

@spongemike2
Copy link
Contributor

@spongemike2 spongemike2 commented Dec 21, 2019

PR Summary

Regarding Invoke-WebRequest:

A change was made to PowerShell Core with commit 2285ece613 that changes the way web request headers are handled when the web request has no body. Previously, they would be ignored. Now, they are processed anyway, including the ContentType header, which makes no sense when there's no body. Now, the headers are processed anyway, and if there's a ContentType header with an empty string value, it will fail.

When a customer tries to pass in a blank (i.e., $null) ContentType to a function that takes a string because they have no body, it gets treated as an empty string, not $null.

This works fin in Windows PowerShell (PS5 and earlier) and PowerShell Core 6, because their code bases don't have commit 2285ece613.

When it fails, they get a message saying that they can avoid this by passing -SkipHeaderValidation, but this flag isn't available in Windows PowerShell, and using it would require a crude hard-coded version check to see if you're on Windows PowerShell or PowerShell Core.

This change merely skips adding headers to the request if the header value is nothing (null or whitespace).

Question: Should we only do this skip for the ContentType header?

Fix #11396

PR Context

Without this, PS scripts that run fine in Windows PowerShell and PowerShell Core 6 break.

PR Checklist

Michael J. Lyons and others added 2 commits December 21, 2019 09:15
…no body.

Regarding `Invoke-WebRequest`:

A change was made to PowerShell Core with commit `2285ece613` that changes the way web request headers are handled when the web request has no body. Previously, they would be ignored. Now, they are processed anyway, including the `ContentType` header, which makes no sense when there's no body. Now, the headers are processed anyway, and if there's a `ContentType` header with an empty string value, it will fail.

When a customer tries to pass in a blank (i.e., `$null`) `ContentType` to a function that takes a `string` because they have no body, it gets treated as an empty string, not `$null`.

This works fin in Windows PowerShell (PS5 and earlier) and PowerShell Core 6, because their code bases don't have commit `2285ece613`.

When it fails, they get a message saying that they can avoid this by passing `-SkipHeaderValidation`, but this flag isn't available in Windows PowerShell, and using it would require a crude hard-coded version check to see if you're on Windows PowerShell or PowerShell Core.

This change merely skips adding headers to the request if the header value is nothing (null or whitespace).

**Question**: Should we only do this *skip* for the `ContentType` header?

PowerShell#11396
Fix for issue 11396 regarding blank ContentType for web request with no body
@ghost ghost assigned daxian-dbw Dec 21, 2019
@msftclas
Copy link

msftclas commented Dec 21, 2019

CLA assistant check
All CLA requirements met.

Copy link
Contributor

@jazzdelightsme jazzdelightsme left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Collecting some discussions about empty HTTP headers from elsewhere:

From the Fetch issue referenced by Steve:

I'm sorry but can someone just say Yes or No whether empty header values are legal HTTP?
Are they allowed by HTTP? Yes. Are they allowed / defined to have meaning for an individual header field? It depends on the field's definition. That said, it's not good practice to do it.

And dotnet seems to currently enforce the "it's not a good idea to do it" opinion (it rejects empty headers).

Suppose at some point, the dotnet Issue was addressed, and they decided to allow empty headers. Would we (PowerShell) then want to expose that ability in PS (to have an empty header value)? We could remove this null/empty check, and then the dotnet API would allow it… and it would be sent to the server. Would the HTTP server allow it? Dunno. So that could be a breaking change, too.

Or we could just say "no, you just can't send empty headers with PS". Given that that is already the case (you can't send an empty HTTP header, else dotnet throws (although I don't know if that behavior has always been that way with dotnet)), and nobody has complained (actually I have no idea if anyone has complained, but it hasn't become a blocking issue that has been addressed--I'm relying on actual PS people to validate what I'm saying here), it seems like a reasonable position to stick to.

Given all that, I think this change looks good.

@spongemike2
Copy link
Contributor Author

One option to make it even more future-proof would be to add another condition to this check, and only ignore blank headers if the body is also empty or non-existent. Then, it will actually behave closer to the PS6 version of the code, which doesn't allow any custom headers if there's no body.

@vexx32
Copy link
Collaborator

vexx32 commented Dec 21, 2019

@spongemike2 I may lack some context or experience in this area, but from memory there are some perfectly valid requests that can be made with headers and no body, aren't there?

@vexx32 vexx32 added the WG-Cmdlets-Utility cmdlets in the Microsoft.PowerShell.Utility module label Dec 21, 2019
@spongemike2
Copy link
Contributor Author

@vexx32, I am not an expert in this area, but if I had to guess, I would say yes, which is presumably why the change was made that broke this. However, headers that have no value? I think that's unlikely to be useful. Rather than specifying a header with a blank value, which dotnetcore doesn't allow, one could get effectively the same information conveyed by omitting the header altogether. Which is why I think (even though I'm not 100% sure) that my change should be safe.

if (!string.IsNullOrWhiteSpace(entry.Value))
{
try
if (SkipHeaderValidation)
Copy link
Collaborator

@iSazonov iSazonov Dec 23, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If our intention is to follow Core (that is to send headers with empty value) the check should be here: Update.

Also please add a test.

Copy link
Member

@SteveL-MSFT SteveL-MSFT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a test? I believe there is an existing test case to validate headers are passed, so just augment it with an empty header.

@SteveL-MSFT SteveL-MSFT added this to the GA-consider milestone Dec 23, 2019
@spongemike2
Copy link
Contributor Author

Test added. Verified that the new test fails prior to this change, and succeeds with it.

@vexx32
Copy link
Collaborator

vexx32 commented Dec 28, 2019

@spongemike2 could you edit the PR title summary to something a bit more descriptive? Thanks! 😊

@spongemike2 spongemike2 changed the title Spongemike2/fix11396 Allow a blank/empty ContentType to be passed to Invoke-WebRequest for compatibility with PS6 and earlier Dec 28, 2019
@spongemike2
Copy link
Contributor Author

@spongemike2 could you edit the PR title summary to something a bit more descriptive? Thanks!

Done.

@spongemike2
Copy link
Contributor Author

Anything else needed?

@iSazonov iSazonov added CL-General Indicates that a PR should be marked as a general cmdlet change in the Change Log and removed WG-Cmdlets-Utility cmdlets in the Microsoft.PowerShell.Utility module labels Jan 1, 2020
@daxian-dbw daxian-dbw merged commit 1376d31 into PowerShell:master Jan 6, 2020
@iSazonov
Copy link
Collaborator

iSazonov commented Jan 6, 2020

I think It is a breaking change and we should add CL-BreakingChange

@spongemike2 spongemike2 deleted the spongemike2/Fix11396 branch January 6, 2020 20:22
@daxian-dbw
Copy link
Member

It looks to me a regression we made in 7.0 time frame and we are fixing it before 7.0 GA. It's a behavior changes only comparing with 7.0 previews, so I think it's not a breaking change. But do let me know if I'm missing something.

@spongemike2
Copy link
Contributor Author

It looks to me a regression we made in 7.0 time frame and we are fixing it before 7.0 GA. It's a behavior changes only comparing with 7.0 previews, so I think it's not a breaking change. But do let me know if I'm missing something.

I agree. It would have been an unknown breaking change if it wasn't fixed before GA, but we caught it and fixed it.

@iSazonov
Copy link
Collaborator

iSazonov commented Jan 7, 2020

I agree if this will be in GA.

@adityapatwardhan adityapatwardhan removed this from the GA-consider milestone Jan 7, 2020
@adityapatwardhan adityapatwardhan added this to the GA-approved milestone Jan 7, 2020
daxian-dbw pushed a commit that referenced this pull request Jan 10, 2020
@daxian-dbw daxian-dbw modified the milestones: GA-approved, 7.0.0-rc.2 Jan 11, 2020
@ghost
Copy link

ghost commented Jan 16, 2020

🎉v7.0.0-rc.2 has been released which incorporates this pull request.:tada:

Handy links:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CL-General Indicates that a PR should be marked as a general cmdlet change in the Change Log

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Breaking change: Invoke-WebRequest -ContentType with empty string now fails

8 participants