4

I have to download a file (using existing Flurl-Http endpoints [1]) whose name contains a "#" which of course has to be escaped to %23 to not conflict with uri-fragment detection.

But Flurl always escapes the rest but not this character, resulting in a non working uri where half of the path and all query params are missing because they got parsed as uri-fragment:

Url url = "http://server/api";
url.AppendPathSegment("item #123.txt");
Console.WriteLine(url.ToString());

Returns: http://server/api/item%20#123.txt

This means a http request (using Flurl.Http) would only try to download the non-existing resource http://server/api/item%20.

Even when I pre-escape the segment, the result still becomes exactly the same:

url.AppendPathSegment("item %23123.txt");
Console.WriteLine(url.ToString());

Again returns: http://server/api/item%20#123.txt.

Any way to stop this "magic" happen?

[1] This means I have delegates/interfaces where input is an existing Flurl.Url instance which I have to modify.

springy76
  • 3,706
  • 2
  • 24
  • 46

2 Answers2

3

It looks like you've uncovered a bug. Here are the documented encoding rules Flurl follows:

  • Query string values are fully URL-encoded.
  • For path segments, reserved characters such as / and % are not encoded.
  • For path segments, illegal characters such as spaces are encoded.
  • For path segments, the ? character is encoded, since query strings get special treatment.

According to the 2nd point, it shouldn't encode # in the path, so how it handles AppendPathSegment("item #123.txt") is correct. However, when you encode the # to %23 yourself, Flurl certainly shouldn't unencode it. But I've confirmed that's what's happening. I invite you to create an issue on GitHub and it'll be addressed.

In the mean time, you could write your own extension method to cover this case. Something like this should work (and you wouldn't even need to pre-encode #):

public static Url AppendFileName(this Url url, string fileName) {
    url.Path += "/" + WebUtility.UrlEncode(fileName);
    return url;
}
Todd Menier
  • 37,557
  • 17
  • 150
  • 173
  • It's great to get feedback from the project maintainer himself :) I spotted code in `Url.EncodeIllegalCharacters` which first calls `Uri.UnescapeDataString` and then packs the result with `Uri.EscapeUriString` -- but according to [https://stackoverflow.com/a/34189188/442376] there never is a valid reason to ever use `EscapeUriString` – springy76 Aug 01 '17 at 21:17
  • I'd again encourage you to log this on GitHub so I remember to take a look at it. I'm certain there was a reason for the 2 calls in that method but I don't remember off the top of my head. Regardless, you've proven it's incorrect and needs to be fixed. – Todd Menier Aug 03 '17 at 02:34
0

I ended up using Uri.EscapeDataString(foo) because suggested WebUtility.UrlEncode replaces space with '+' which I didn't want to.

flam3
  • 1,897
  • 2
  • 19
  • 26