I have encountered an interesting situation where I get NRE from Uri.TryCreate method when it's supposed to return false.
You can reproduce the issue like below:
Uri url;
if (Uri.TryCreate("http:Ç", UriKind.RelativeOrAbsolute, out url))
{
Console.WriteLine("success");
}
I guess it's failing during the parse, but when I try "http:A" for example, it returns true and parses it as relative url. Even if fails on parse it should just return false as I understand, what could be the problem here? This seems like a bug in the implementation cause documentation doesn't mention about any exception on this method.
The error occurs in .NET 4.6.1 but not 4.0
This is a bug in the .NET framework. You can open a ticket on MicrosoftConnect.
The exception will be raised in this method
void Systen.Uri.CreateUriInfo(System.Uri.Flags cF)
on line 2290 (inspect the reference source) executing following statement:
// This is NOT an ImplicitFile uri
idx = (ushort)m_Syntax.SchemeName.Length;
At this time, the m_Syntax object will be null, because during parsing, it will be discarded.
Method
void InitializeUri(ParsingError err, UriKind uriKind, out UriFormatException e)
line 121:
if (m_Syntax.IsSimple)
{
if ((err = PrivateParseMinimal()) != ParsingError.None)
{
if (uriKind != UriKind.Absolute && err <= ParsingError.LastRelativeUriOkErrIndex)
{
// RFC 3986 Section 5.4.2 - http:(relativeUri) may be considered a valid relative Uri.
m_Syntax = null; // convert to relative uri
e = null;
m_Flags &= Flags.UserEscaped; // the only flag that makes sense for a relative uri
}
// ...
}
// ...
}
The PrivateParseMinimal() method returns ParsingError.BadAuthority and uriKind == UriKind.RelativeOrAbsolute by your specification.
The PrivateParseMinimal() method looks for any of the following character sequences: "//", "\", "/\", "/". And since there are no such sequences in your input string, a ParsingError.BadAuthority code will be returned.
Related
I have a class:
class Options
{
// Remainder omitted (verb1, verb2, verb3)
[HelpVerbOption]
public string GetUsage(string verb)
{
return HelpText.AutoBuild(this, verb);
}
}
The docs say:
[...] The parser will pass null to master class GetUsage(string) also if
the user requested the help index with:
$ git help
or the verb command if the user requested explicitly
instructions on how to use a particular verb:
$ git help commit
[...]
Then, I typed MyApp.exe help verb1, but I could see only the base help (that looked like I typed the wrong verb, or help verb, or something). Rather, I expect it to show the help message related to specified verb. Why isn't it working properly?
For me it works using the mentioned approach, but only if I call my app without the --help-option (for instance MyApp batch). When I use MyApp --help batch the behaviour is as described by you.
However we can´t seem to get the same to work for the help-option.
EDIT: I managed to get this working by modifying the code of Commandline.Parser.cs with the following:
private bool TryParseHelpVerb(string[] args, object options, Pair<MethodInfo, HelpVerbOptionAttribute> helpInfo, OptionMap optionMap)
{
var helpWriter = _settings.HelpWriter;
if (helpInfo != null && helpWriter != null)
{
if (string.Compare(args[0], helpInfo.Right.LongName, GetStringComparison(_settings)) == 0)
{
// User explicitly requested help
var verb = args.FirstOrDefault(); // <----- change this to args[1];
if (verb != null)
{
var verbOption = optionMap[verb];
if (verbOption != null)
{
if (verbOption.GetValue(options) == null)
{
// We need to create an instance also to render help
verbOption.CreateInstance(options);
}
}
}
DisplayHelpVerbText(options, helpInfo, verb);
return true;
}
}
return false;
}
The problem appears at the line
var verb = args.FirstOrDefault();
As the very first argument (args[0]) is interpreteted as the verb or better the action (as described in the docs) verb will allways be help here. So we replace this by args[1] which contains the actual verb, for example commit.
EDIT2: To make this work for --help also we should also trim the first arg (args[0]) from the --character
if (string.Compare(args[0].Trim('-'), helpInfo.Right.LongName, GetStringComparison(_settings)) == 0)
I currently have a button in a table view. I have a custom cell class and an update cell method.
public void UpdateCell (string subtitle2)
{
call.SetTitle(subtitle2, UIControlState.Normal);
call.TouchUpInside += (object sender, EventArgs e) => {
UIApplication.SharedApplication.OpenUrl(new NSUrl("tel:" + subtitle2));
};
}
However, when i run this segment of code, i get an error.
Could not initialize an instance of the type
'MonoTouch.Foundation.NSUrl': the native 'initWithString:' method
returned nil. It is possible to ignore this condition by setting
MonoTouch.ObjCRuntime.Class.ThrowOnInitFailure to false.
Try this:
public void UpdateCell (string subtitle2)
{
//Makes a new NSUrl
var callURL = new NSUrl("tel:" + subtitle2);
call.SetTitle(subtitle2, UIControlState.Normal);
call.TouchUpInside += (object sender, EventArgs e) => {
if (UIApplication.SharedApplication.CanOpenUrl (callURL)) {
//After checking if phone can open NSUrl, it either opens the URL or outputs to the console.
UIApplication.SharedApplication.OpenUrl(callURL);
} else {
//OUTPUT to console
Console.WriteLine("Can't make call");
}
};
}
Not sure if phone calls can be simulated in the iPhone Simulator, otherwise just connect your device :-)
As mentioned in other answers, NSUrl cannot handle spaces (and some other symbols) in URL string. That symbols should be encoded before passing to the NSUrl constructor. You can do the encoding with the NSString.CreateStringByAddingPercentEscapes method.
Another way is using of the .NET Uri class:
new NSUrl(new Uri("tel:" + subtitle2).AbsoluteUri)
The issue you might encounter here is parsing of the schema by the Uri class. To avoid it, don't use ://, so, URL strings should look as "tel:(123) 345-7890", not as "tel://(123) 345-7890"
Try
UIApplication.SharedApplication.OpenUrl(new NSUrl("tel://" + subtitle2));
This error means that calling initWithString: returned nil. ObjC init* methods can return nil while .NET constructors can't (and throws).
In general ObjC returns nil on init* selectors when the input parameters are invalid. In this case I suspect your URL format is invalid.
What's the content of your subtitle2 variable ?
I resolved the same problem, after trying various ways, by finally figuring out that there should not be any spaces (instead of e.g. 1800 111 222 333, should be like 1800111222333) between numbers to make call in iOS.
number = number.Replace(" ", "");
NSUrl url = new NSUrl(string.Format(#"telprompt://{0}", number));
return UIApplication.SharedApplication.OpenUrl(url);
I'm trying to use VirtualPathUtility.ToAbsolute to resolve app-relative paths, such as ~/MyPage.aspx, to application-absolute paths, such as /MySite/MyApp/MyPage.aspx. However, with some paths, I receive an HttpException saying that my path is "not a valid virtual path". Examples:
// This works:
var abs1 = VirtualPathUtility.ToAbsolute("~/MyPage.aspx#anchor");
// This errors:
var abs2 = VirtualPathUtility.ToAbsolute("~/MyPage.aspx?key=value");
What's going on?
Because you're using .NET 3.5, you're using the 2.0 System.Web assembly, which has the defect that ? is considered an illegal path character by this method. This is mentioned in the community comments on the version-specific MSDN page.
By disassembling, it can be seen that the call ends up in the (internal) VirtualPath.Create, which has:
else if (VirtualPath.ContainsIllegalVirtualPathChars(virtualPath))
{
throw new HttpException(System.Web.SR.GetString("Invalid_vpath", new object[1]
{
(object) virtualPath
}));
}
which references
private static char[] s_illegalVirtualPathChars = new char[4]
{
':',
'?',
'*',
char.MinValue
};
Some of these can reasonably be regarded as bad characters for a path, but ? shouldn't really be so rejected.
Disassembly of the 4.0 System.Web shows that VirtualPath.Create has been rewritten to be more discerning.
This web.archive capture of a now-defunct blogs.msdn post shows one of the earliest mentions of this problem. The MS employee responds:
Sunday, February 26, 2006 11:49 PM by DmitryR Exception on ~/path?qs
is a bug that I'll need to fix...
The easiest fix is to save/restore query string in
ResolveAppRelativeLinkToUrl around the call to
VirtualPathUtility.ToAbsolute.
A workaround is to use fully qualified UTLs instead of "~/...".
Thanks,
Dmitry
where ResolveAppRelativeLinkToUrl refers to the reporter's code's method name.
Another workaround would be to replace ? with a safe token before the call to VirtualPathUtility.ToAbsolute, and reverse the replacement afterwards:
public static string SafeToAbsolute(string path)
{
var madeSafe = path.Replace("?", "UNLIKELY_TOKEN");
var absolute = VirtualPathUtility.ToAbsolute(madeSafe);
var restored = absolute.Replace("UNLIKELY_TOKEN", "?");
return restored;
}
choosing a suitably unlikely token for your application.
How do you check that a uri string is valid (that you can feed it to the Uri constructor)?
So far I only have the following but for obvious reasons I'd prefer a less brute way:
Boolean IsValidUri(String uri)
{
try
{
new Uri(uri);
return true;
}
catch
{
return false;
}
}
I tried Uri.IsWellFormedUriString but it doesn't seem to like everything that you can throw at the constructor. For example:
String test = #"C:\File.txt";
Console.WriteLine("Uri.IsWellFormedUriString says: {0}", Uri.IsWellFormedUriString(test, UriKind.RelativeOrAbsolute));
Console.WriteLine("IsValidUri says: {0}", IsValidUri(test));
The output will be:
Uri.IsWellFormedUriString says: False
IsValidUri says: True
Update/Answer
The Uri constructor uses kind Absolute by default. This was causing a discrepancy when I tried using Uri.TryCreate and the constructor. You do get the expected outcome if you match the UriKind for both the constructor and TryCreate.
A well-formed URI implies conformance with certain RFCs. The local path in your example is not conformant with these. Read more in the IsWellFormedUriString documentation.
A false result from that method does not imply that the Uri class will not be able to parse the input. While the URI input might not be RFC conformant, it still can be a valid URI.
Update: And to answer your question - as the Uri documentation shows, there is a static method called TryCreate that will attempt exactly what you want and return true or false (and the actual Uri instance if true).
Since the accepted answer doesn't provide an explicit example, here is some code to validate URIs in C#:
Uri outUri;
if (Uri.TryCreate("ThisIsAnInvalidAbsoluteURI", UriKind.Absolute, out outUri)
&& (outUri.Scheme == Uri.UriSchemeHttp || outUri.Scheme == Uri.UriSchemeHttps))
{
//Do something with your validated Absolute URI...
}
Assuming we only want to support absolute URI and HTTP requests, here is a function that does what you want:
public static bool IsValidURI(string uri)
{
if (!Uri.IsWellFormedUriString(uri, UriKind.Absolute))
return false;
Uri tmp;
if (!Uri.TryCreate(uri, UriKind.Absolute, out tmp))
return false;
return tmp.Scheme == Uri.UriSchemeHttp || tmp.Scheme == Uri.UriSchemeHttps;
}
In my case I just wanted to test the uri, I don't want to slow down the application testing the uri.
Boolean IsValidUri(String uri){
return Uri.IsWellFormedUriString(uri, UriKind.Absolute);
}
Try it:
private bool IsValidUrl(string address)
{
return Uri.IsWellFormedUriString(address, UriKind.RelativeOrAbsolute);
}
In your case the uri argument is an absolute path which refers to a file location, so as per the doc of the method it returns false. Refer to this
I know, the title sounds like this question has been addressed many times. But I am struggling with a specific case and I am very confused over it. Hopefully a seasoned C#'er could point me in the correct direction.
I have the code:
string serviceURL = "https://www.domain.com/service/tables/bucketname%2Ftables%2Ftesttable/imports";
HttpWebRequest dataRequest = (HttpWebRequest)WebRequest.Create(serviceURL);
Now when I quickwatch dataRequest, I see that:
RequestUri: {https://www.domain.com/service/tables/bucketname/tables/testtable/imports}
And it looks like the HttpWebRequest has changed both the %2F to /. However, the server needs the requested Uri to be exactly as serviceURL is written, containing the %2F.
Is there any way to get the HttpWebRequest class to call the Url:
https://www.domain.com/service/tables/bucketname%2Ftables%2Ftesttable/imports
Many thanks! I am at a complete loss here...
-Brett
Kyle posted the answer in a comment, so to make it official:
GETting a URL with an url-encoded slash
It's a weird work around, but nevertheless gets the job done.
As long as the problem lies in %2F being unescaped to "/" there are solutions out there. One involving a hack and for newer versions of .Net, an app.config setting. Check here: How to make System.Uri not to unescape %2f (slash) in path?
However I have still to figure out how to prevent it unescaping some specifically escaped characters, like '(' and ')' (%28 and %29). I have tried all the settings and hacks that I found out there to prevent the Uri class from delivering a partially unescaped path for the WebRequest. The solutions will happily prevent %2F being unescaped, but not %28 and %29 and possible most of the other chars being specifically escaped.
It seems like the WebRequest is specifically asking for 1 value from the Uri object to create the "GET /path HTTP/1.1" syntax: Uri.PathAndQuery which again calls its UriParser.GetComponents.
If you want to download from mediafire and it contains the chars %28 and %29 you will get into a infinite redirect loop as .Net keeps changing %28 and %29 to '(' and ')' and following the redirect (exception: "Too many automatic redirections were attempted").
So this is a solution for those who are stuck and have not been able to find a way to prevent the unescape of some characters.
The only way I have found to override this (currenly using .Net 4.6) and deliver my own PathAndQuery has been a combination of inherting UriParser and hacking its use.
public sealed class MyUriParser : System.UriParser
{
private UriParser _originalParser;
private MethodInfo _getComponentsMethod;
public MyUriParser(UriParser originalParser) : base()
{
if (_originalParser == null)
{
_originalParser = originalParser;
_getComponentsMethod = typeof(UriParser).GetMethod("GetComponents", BindingFlags.NonPublic | BindingFlags.Instance);
if (_getComponentsMethod == null)
{
throw new MissingMethodException("UriParser", "GetComponents");
}
}
}
private static Regex rx = new Regex(#"^(?<Scheme>[^:]+):(?://((?<User>[^#/]+)#)?(?<Host>[^#:/?#]+)(:(?<Port>\d+))?)?(?<Path>([^?#]*)?)?(\?(?<Query>[^#]*))?(#(?<Fragment>.*))?$",RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.Singleline);
private Match m = null;
protected override string GetComponents(Uri uri, UriComponents components, UriFormat format)
{
var original = (string)_getComponentsMethod.Invoke(_originalParser, BindingFlags.InvokeMethod, null, new object[] { uri, components, format }, null);
if (components == UriComponents.PathAndQuery)
{
var reg = rx.Match(uri.OriginalString);
var path = reg.Groups["Path"]?.Value;
var query = reg.Groups["Query"]?.Value;
if (path != null && query != null) return $"{path}?{query}";
if (query == null) return $"{path}";
return $"{path}";
}
return original;
}
}
And then hacking it into the Uri instance by replacing its UriParser with this one.
public static Uri CreateUri(string url)
{
var uri = new Uri(url);
if (url.Contains("%28") || url.Contains("%29"))
{
var originalParser = ReflectionHelper.GetValueByReflection(uri, "m_Syntax") as UriParser;
var parser = new MyUriParser(originalParser);
ReflectionHelper.SetValueByReflection(parser, "m_Scheme", "http");
ReflectionHelper.SetValueByReflection(parser, "m_Port", 80);
ReflectionHelper.SetValueByReflection(uri, "m_Syntax", parser);
}
return uri;
}
Due to the way UriParser works, it normally needs to register to have its port and scheme name set, so these 2 values has to be set by reflection as we are not registering it the correct way. I have not found a way to register "http" as it already exist. The ReflectionHelper is just a class I have but can be quickly replaced with normal reflection code.
Then call it like this:
HttpWebRequest dataRequest = (HttpWebRequest)WebRequest.Create(CreateUri(serviceURL));
string serviceURL = Uri.EscapeUriString("https://www.domain.com/service/tables/bucketname%2Ftables%2Ftesttable/imports");