I have an URL containing several slash characters (/
) as a part of the filename (not the URL). But when I send http request, the percent-encoded %2F
is translated to /
before the request dispatch, therefore generating a wrong URL.
How can I make a literal http request ignoring the percent-encoded values in PowerShell?
Actual URL used (Chromium browser):
I have tried Invoke-WebRequest
cmdlet:
Invoke-WebRequest -Uri $ChromeUrl -OutFile $FilePath -Verbose
VERBOSE: GET https://www.googleapis.com/download/storage/v1/b/chromium-browser-continuous/o/Win_x64/292817/chrome-win32.zip?generation=1409504089694000&alt=media with 0-byte payload1`
Not found error.
Also tried WebClient
's DownloadFile
method:
$wclient = New-Object System.Net.WebClient
$wclient.DownloadFile($ChromeUrl, $FilePath)
Returns 404 due to wrong URL requested again.
Workaround 1 (successful)
Reflection-based workarounds provided by briantist and Tanuj Mathur are both working great. The latter one:
$UrlFixSrc = @"
using System;
using System.Reflection;
public static class URLFix
{
public static void ForceCanonicalPathAndQuery(Uri uri)
{
string paq = uri.PathAndQuery;
FieldInfo flagsFieldInfo = typeof(Uri).GetField("m_Flags", BindingFlags.Instance | BindingFlags.NonPublic);
ulong flags = (ulong) flagsFieldInfo.GetValue(uri);
flags &= ~((ulong) 0x30);
flagsFieldInfo.SetValue(uri, flags);
}
}
"@
Add-Type -TypeDefinition $UrlFixSrc-Language CSharp
[URLFix]::ForceCanonicalPathAndQuery([URI]$ChromeUrl)
Invoke-WebRequest -Uri $ChromeUrl -OutFile $FilePath -Verbose
VERBOSE: GET https://www.googleapis.com/download/storage/v1/b/chromium-browser-continuous/o/Win_x64%2F292640%2Fchrome-win32.zip?generation=1409351584147000&alt=media
Workaround 2 (successful)
More clean solution (offered by Tanuj Mathur), but requires access to system files, is by adding a config file %SystemRoot%System32WindowsPowerShellv1.0powershell.exe.config
with the following content:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<uri>
<schemeSettings>
<add name="http" genericUriParserOptions="DontUnescapePathDotsAndSlashes" />
<add name="https" genericUriParserOptions="DontUnescapePathDotsAndSlashes" />
</schemeSettings>
</uri>
</configuration>
Corresponding modifications has to be done in powerhsell_ise.exe.config
for it to work in ISE.
Workaround 3 (failed)
I thought its a System.URI
class constructor problem that is called upon implicit casting, which translates the escaped values. Tried an overloaded variant Uri ([String]uriString, [Boolean]dontEscape)
. But there was no difference. The same outcome with or without dontEscape
argument.
$uri = new-object System.Uri($ChromeUrl, $true)
$uri | Format-List OriginalString, AbsoluteUri
OriginalString : https://www.googleapis.com/download/storage/v1/b/chromium-browser-continuous/o/Win_x64%2F292817%2Fchrome-win32.zip?generation=1409504089694000&alt=media
AbsoluteUri : https://www.googleapis.com/download/storage/v1/b/chromium-browser-continuous/o/Win_x64/292817/chrome-win32.zip?generation=1409504089694000&alt=media
Workaround 4 (failed)
Also tried to fool URI parser by replacing percent character with its percent-encoded value %25
. But then it ignored everything completely.
Invoke-WebRequest -Uri $ChromeUrl.Replace('%', '%25') -OutFile $DownloadPath -Verbose
VERBOSE: GET https://www.googleapis.com/download/storage/v1/b/chromium-browser-continuous/o/Win_x64%252F292817%252Fchrome-win32.zip?generation=1409504089694000&alt=media with 0-byte pa yload
Workaround 5 (not implemented)
The only way I found that requests URL properly is through Internet Explorer instance.
$ie = New-Object -ComObject InternetExplorer.Application
$ie.Visible = $true
$ie.Silent = $false
$ie.Navigate2($ChromeUrl)
But then I don't know how to automate the 'Saves as' button click and save it to desired path. Also, even if implemented, I don't feel like this is a good solution. What happens when IE is already running or uninstalled from the system?
See Question&Answers more detail:os