Percent-encoded slash ("/") is decoded before the request dispatch Percent-encoded slash ("/") is decoded before the request dispatch powershell powershell

Percent-encoded slash ("/") is decoded before the request dispatch


I've been playing around with your code for the last few hours, and it's a doozy. The given code and it's variants all pass when run in the Powershell ISE, but fail on the Powershell console.The issue itself seems to be the one documented on Microsoft Connect here.

Interestingly, as per user Glenn Block's answer on a related issue, this bug was fixed in .NET Framework 4.5. You can check the version of the .NET framework being used by your Powershell by running the command $PSVersionTable. As long as the CLRVersion value is of the form 4.0.30319.x, where x > 1700, then you are running v4.5 of the framework.

I'm running Powershell v4.0 on .NET framework 4.5 on my machine, so that explains why Powershell ISE shows the correct behaviour, but I was not able to figure out why Powershell console does not. I verified the .NET assemblies loaded by both, and they seem to be the same.

As things stand, we have two options. One is to use reflection and set a private field on the .Net class to prevent this behaviour (as outlined in this answer). The other is to use the workaround listed in the Microsoft Connect issue. This involves the following steps:

  1. Go to your Powershell install folder (this was "C:\Windows\System32\WindowsPowerShell\v1.0\" on my machine). This folder should have the file powershell.exe in it.
  2. Create a new text file in this folder, and name it powershell.exe.config
  3. Open this file in a text editor, and paste the following text into it: <?xml version="1.0" encoding="utf-8" ?> <configuration> <uri> <schemeSettings> <add name="http" genericUriParserOptions="DontUnescapePathDotsAndSlashes" /> <add name="https" genericUriParserOptions="DontUnescapePathDotsAndSlashes" /> </schemeSettings> </uri></configuration>

  4. Save this file. Close ALL running instances of Powershell.

  5. Start a new instance of Powershell. This will cause Powershell to detect the config file you created and parse it. The config entries basically tell the .NET libraries to disable the automatic unescaping of HTTP and HTTPS uri's.
  6. Run your script. You should no longer see the issue with the Uris.


If you're going to use PowerShell you can also do Workaround 1 in pure PowerShell:

function UrlFix([Uri]$url) {    $url.PathAndQuery | Out-Null    $m_Flags = [Uri].GetField("m_Flags", $([Reflection.BindingFlags]::Instance -bor [Reflection.BindingFlags]::NonPublic))    [uint64]$flags = $m_Flags.GetValue($url)    $m_Flags.SetValue($url, $($flags -bxor 0x30))}UrlFix $ChromeUrlInvoke-WebRequest -Uri $ChromeUrl -OutFile $FilePath -Verbose


Wow, this is quite a conundrum. There is a bug report about this on Microsoft Connect. It seems there is a workaround for ASP.net which won't help you in PowerShell.

Here's where it gets really weird though. I am running PowerShell 4.0. I can reproduce this problem when running in the console host. However, if I run the exact same code in the ISE Host, it works flawlessly.

I don't have any idea how or why. I even remoted into another system not on my network to make sure I didn't somehow change anything weird on my system. Same result. Bottom line:

$a = 'https://www.googleapis.com/download/storage/v1/b/chromium-browser-continuous/o/Win_x64%2F292817%2Fchrome-win32.zip?generation=1409504089694000&alt=media'Invoke-WebRequest -Uri $a

This works in ISE, does not work in console host. I even tried it with -UseBasicParsing to make sure it wasn't a weird quirk of DOM parsing.

A Dirty Workaround

I took the C# code in Simon Maurier's answer to "How to make System.Uri not to unescape %2f (slash) in path?" and I adapted it for use in PowerShell:

$uriFixerDef = @'using System;using System.Reflection;public class UriFixer{    private const int UnEscapeDotsAndSlashes = 0x2000000;    private const int SimpleUserSyntax = 0x20000;    public static void LeaveDotsAndSlashesEscaped(Uri uri)    {        if (uri == null)            throw new ArgumentNullException("uri");        FieldInfo fieldInfo = uri.GetType().GetField("m_Syntax", BindingFlags.Instance | BindingFlags.NonPublic);        if (fieldInfo == null)            throw new MissingFieldException("'m_Syntax' field not found");        object uriParser = fieldInfo.GetValue(uri);        fieldInfo = typeof(UriParser).GetField("m_Flags", BindingFlags.Instance | BindingFlags.NonPublic);        if (fieldInfo == null)            throw new MissingFieldException("'m_Flags' field not found");        object uriSyntaxFlags = fieldInfo.GetValue(uriParser);        // Clear the flag that we do not want        uriSyntaxFlags = (int)uriSyntaxFlags & ~UnEscapeDotsAndSlashes;        uriSyntaxFlags = (int)uriSyntaxFlags & ~SimpleUserSyntax;        fieldInfo.SetValue(uriParser, uriSyntaxFlags);    }}'@Add-Type -TypeDefinition $uriFixerDef$u = 'https://www.googleapis.com/download/storage/v1/b/chromium-browser-continuous/o/Win_x64%2F292817%2Fchrome-win32.zip?generation=1409504089694000&alt=media'[UriFixer]::LeaveDotsAndSlashesEscaped($u)Invoke-WebRequest -Uri $u

I first tested it in ISE and then later found that ISE was working no matter what. So I did in fact try this in a clean console host environment and before invoking the method, I got notfound. After invoking, worked.

As it says in the linked answer, this is an ugly hack, may break in future versions, etc.

I do hope it helps, this is an interesting issue.