Wasted 2 hours trying to track down the following error when making a simple WebClient DownloadFile request to an Amazon S3 url:
The remote server returned an error: (403) Forbidden.
Offending code and Url:
Uri uu = new Uri("https://zencoder-live.s3.amazonaws.com:443/add9d5d739193c13fcde60d3d7ff5ba7%2Ffe33f4d52e1cff0ef06592ed4041a7dc.mp4?Signature=b%2FXw9ylREb4up4QDw6Tyv9GyQhU%3D&Expires=1291150754&AWSAccessKeyId=AKIAIIEXNN2J4YDTRUVQ");
using (WebClient wClient = new WebClient())
{
wClient.DownloadFile(uu, @"C:\output.mp4");
}
I was able to use
Fiddler to compare Firefox's request versus my .NET application's request.
Firefox:GET /add9d5d739193c13fcde60d3d7ff5ba
7%2Ffe33f4d52e1cff0ef06592ed4041a7dc.mp4?Signature=b%2FXw9ylREb4up4QDw6Tyv9GyQhU%3D&Expires=1291150754&AWSAccessKeyId=AKIAIIEXNN2J4YDTRUVQ HTTP/1.1
.NET WebClient:GET /add9d5d739193c13fcde60d3d7ff5ba
7/fe33f4d52e1cff0ef06592ed4041a7dc.mp4?Signature%3Db%2FXw9ylREb4up4QDw6Tyv9GyQhU%3D%26Expires%3D1291150754%26AWSAccessKeyId%3DAKIAIIEXNN2J4YDTRUVQ HTTP/1.1
Notice that .NET is escaping my Url. Particularly the forward slash (%2F). You used to be able to pass a
dontEscape parameter to the new Uri constructor but now that parameter is deprecated and is always false.
Luckily I came across a
workaround on StackOverflow by
Rasmus Faber:
Uri uu = new Uri("https://zencoder-live.s3.amazonaws.com:443/add9d5d739193c13fcde60d3d7ff5ba7%2Ffe33f4d52e1cff0ef06592ed4041a7dc.mp4?Signature=b%2FXw9ylREb4up4QDw6Tyv9GyQhU%3D&Expires=1291150754&AWSAccessKeyId=AKIAIIEXNN2J4YDTRUVQ");
ForceCanonicalPathAndQuery(uu);
using (WebClient wClient = new WebClient())
{
wClient.DownloadFile(uu, @"C:\output.mp4");
}
void ForceCanonicalPathAndQuery(Uri uri){
string paq = uri.PathAndQuery; // need to access PathAndQuery
FieldInfo flagsFieldInfo = typeof(Uri).GetField("m_Flags", BindingFlags.Instance | BindingFlags.NonPublic);
ulong flags = (ulong) flagsFieldInfo.GetValue(uri);
flags &= ~((ulong) 0x30); // Flags.PathNotCanonical|Flags.QueryNotCanonical
flagsFieldInfo.SetValue(uri, flags);
}
Now the code downloads the file like without the error!