PageRenderTime 43ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/Library/Internal/Network/HttpServer/URLParser.cs

http://github.com/sones/sones
C# | 422 lines | 241 code | 115 blank | 66 comment | 54 complexity | 8b5c159f296beec9f382be1ec7dbaf16 MD5 | raw file
Possible License(s): AGPL-3.0, Apache-2.0, LGPL-3.0
  1. /*
  2. * sones GraphDB - Community Edition - http://www.sones.com
  3. * Copyright (C) 2007-2011 sones GmbH
  4. *
  5. * This file is part of sones GraphDB Community Edition.
  6. *
  7. * sones GraphDB is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU Affero General Public License as published by
  9. * the Free Software Foundation, version 3 of the License.
  10. *
  11. * sones GraphDB is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with sones GraphDB. If not, see <http://www.gnu.org/licenses/>.
  18. *
  19. */
  20. using System;
  21. using System.Collections.Generic;
  22. using System.Linq;
  23. using System.Reflection;
  24. using sones.Library.LanguageExtensions;
  25. namespace sones.Library.Network.HttpServer
  26. {
  27. /// <summary>
  28. /// A URL node which stores some childnodes and a callback
  29. /// </summary>
  30. public class UrlNode
  31. {
  32. public Dictionary<String, UrlNode> ChildNodes { get; set; }
  33. public MethodInfo Callback { get; set; }
  34. public Boolean NeedsExplicitAuthentication { get; set; }
  35. public UrlNode()
  36. {
  37. ChildNodes = new Dictionary<String, UrlNode>();
  38. }
  39. }
  40. /// <summary>
  41. /// The URLParser class which parses some URLs
  42. /// </summary>
  43. public class UrlParser
  44. {
  45. #region data
  46. //NLOG: temporarily commented
  47. //private static Logger //_Logger = LogManager.GetCurrentClassLogger();
  48. private readonly char[] _separators;
  49. /// <summary>
  50. /// &lt;WebMethod, &lt;location, UrlNode&gt;&gt;
  51. /// </summary>
  52. private readonly Dictionary<String, Dictionary<String, UrlNode>> _rootNodes;
  53. #endregion
  54. #region constructors
  55. public UrlParser(char[] separators)
  56. {
  57. _rootNodes = new Dictionary<String, Dictionary<String, UrlNode>>();
  58. _separators = separators;
  59. }
  60. #endregion
  61. #region Initialize the parser by adding some URL definitions
  62. /// <summary>
  63. /// Adds a url
  64. /// </summary>
  65. /// <param name="url">The url</param>
  66. /// <param name="method">The method which should be invoked for any match</param>
  67. /// <param name="needsExplicitAuthentication"></param>
  68. /// <param name="webMethod"></param>
  69. public void AddUrl(string url, MethodInfo method, bool needsExplicitAuthentication, string webMethod = "GET")
  70. {
  71. url = url.ToLower();
  72. var valueArray = url.Split(_separators);
  73. if (valueArray.Length == 0)
  74. {
  75. return;
  76. }
  77. var node = new Dictionary<String, UrlNode>();
  78. if (_rootNodes.ContainsKey(webMethod))
  79. {
  80. node = _rootNodes[webMethod];
  81. }
  82. else
  83. {
  84. _rootNodes.Add(webMethod, node);
  85. }
  86. AddNode(node, valueArray, method, needsExplicitAuthentication);
  87. }
  88. /// <summary>
  89. ///
  90. /// </summary>
  91. /// <param name="urlNodes"></param>
  92. /// <param name="urlParts"></param>
  93. /// <param name="methodInfo"></param>
  94. /// <param name="needsExplicitAuthentication"></param>
  95. private static void AddNode(IDictionary<string, UrlNode> urlNodes, IEnumerable<String> urlParts, MethodInfo methodInfo, Boolean needsExplicitAuthentication)
  96. {
  97. var val = urlParts.FirstOrDefault();
  98. if (val == null)
  99. {
  100. throw new ArgumentException("The enumerable does not contain a value.", "urlParts");
  101. }
  102. #region SpeedUp by removing all between {...}
  103. if (val.StartsWith("{") && val.EndsWith("}")) // something like /{....} or .{....} or ?{.....} will be changed to /{} .{} ?{}
  104. {
  105. val = "{}";
  106. }
  107. #endregion
  108. UrlNode curUrlNode;
  109. #region Use an existing node or add a new one
  110. if (urlNodes.ContainsKey(val))
  111. {
  112. curUrlNode = urlNodes[val];
  113. }
  114. else
  115. {
  116. curUrlNode = new UrlNode
  117. {
  118. NeedsExplicitAuthentication = needsExplicitAuthentication
  119. };
  120. urlNodes.Add(val, curUrlNode);
  121. }
  122. #endregion
  123. #region If there are some more parts proceed or set the methodInfo for the last part
  124. if (urlParts.Count() > 1)
  125. {
  126. // there are still some more parts of the URL
  127. AddNode(curUrlNode.ChildNodes, urlParts.Skip(1), methodInfo, needsExplicitAuthentication);
  128. }
  129. else
  130. {
  131. // this was the last one - take the methodInfo
  132. curUrlNode.Callback = methodInfo;
  133. }
  134. #endregion
  135. }
  136. #endregion
  137. #region Get a callback for an url on the parser
  138. /// <summary>
  139. /// Get the next matching callback for the <paramref name="url"/>
  140. /// </summary>
  141. /// <param name="url">The url</param>
  142. /// <param name="webMethod"></param>
  143. /// <returns>The methodInfo callback and the optional parameters</returns>
  144. public Tuple<UrlNode, List<Object>> GetCallback(String url, String webMethod = "GET")
  145. {
  146. #region What should happen with ? params???
  147. if (url.IndexOf('?') > 0)
  148. {
  149. url = url.Substring(0, url.IndexOf('?'));
  150. }
  151. #endregion
  152. #region The WCF rest version seems to replace al // with / - so do the same
  153. //url = url.Replace("//", "/");
  154. #endregion
  155. if (!_rootNodes.ContainsKey(webMethod)) // try to find the method
  156. {
  157. return null;
  158. }
  159. var urlParts = url.Split(_separators); // skip the first one because this is all in front of the first "/" and this is odd
  160. return GetCallback(_rootNodes[webMethod], urlParts, new List<Object>(), url, 0);
  161. }
  162. /// <summary>
  163. /// This will check
  164. /// </summary>
  165. /// <param name="urlNodes"></param>
  166. /// <param name="urlParts">the splittet</param>
  167. /// <param name="parameters">The parameters, parsed from the url</param>
  168. /// <param name="url">The uriginal url - this is needed to get the real parameter before splitting. Due to the split we lost the seperator char</param>
  169. /// <param name="posInUrl">The current pos in the url - this is needed to get the real parameter before splitting. Due to the split we lost the seperator char</param>
  170. /// <returns></returns>
  171. private static Tuple<UrlNode, List<Object>> GetCallback(IDictionary<string, UrlNode> urlNodes, IEnumerable<String> urlParts, List<Object> parameters, String url, Int32 posInUrl)
  172. {
  173. var val = urlParts.FirstOrDefault();
  174. if (val == null)
  175. {
  176. throw new ArgumentException("The enumerable does not contain a value.", "urlParts");
  177. }
  178. val = val.ToLower();
  179. UrlNode curUrlNode;
  180. if (urlNodes.ContainsKey(val))
  181. {
  182. #region The current url node has this part of the URL
  183. curUrlNode = urlNodes[val];
  184. posInUrl += (val.Length == 0) ? 1 : val.Length + 1; // for just a / (which is now a empty string because of the split)
  185. #endregion
  186. }
  187. else if (urlNodes.ContainsKey("{}"))
  188. {
  189. #region If there is a wildcard in this node, use this
  190. curUrlNode = urlNodes["{}"];
  191. if (curUrlNode.ChildNodes == null || curUrlNode.ChildNodes.Count == 0)
  192. {
  193. // this is the last parameter - so add the rest of the url as well
  194. parameters.Add(url.Substring(posInUrl));
  195. return new Tuple<UrlNode, List<object>>(curUrlNode, parameters);
  196. }
  197. // just add this part and proceed
  198. if (url.Length > posInUrl)
  199. {
  200. //parameters.Add(url.Substring(posInUrl, (val.Length == 0) ? 1 : val.Length));
  201. parameters.Add(url.Substring(posInUrl, val.Length));
  202. posInUrl += val.Length + 1; // add 1 for the missing seperator char
  203. }
  204. else
  205. {
  206. parameters.Add("");
  207. }
  208. #endregion
  209. }
  210. else
  211. {
  212. #region The node does not have this part and a wildcard neither
  213. return null;
  214. #endregion
  215. }
  216. if (urlParts.CountIsGreater(1) && !curUrlNode.ChildNodes.IsNullOrEmpty())
  217. {
  218. #region There are more url parts AND childs in the current node
  219. // we have some more childs defined
  220. Tuple<UrlNode, List<object>> retval;
  221. var newParams = new List<Object>();
  222. do
  223. {
  224. #region As long as we can go deeper lets do it
  225. retval = GetCallback(curUrlNode.ChildNodes, urlParts.Skip(1), newParams, url, posInUrl);
  226. if (retval != null)
  227. continue;
  228. #region There is no hit for the current nodes childs and the next url parts
  229. if (!curUrlNode.ChildNodes.ContainsKey("{}"))
  230. {
  231. #region This part is still not valid, return null to proceed with the predecessor level
  232. return null;
  233. #endregion
  234. }
  235. #region But the childs contains a wildcard we could use
  236. curUrlNode = curUrlNode.ChildNodes["{}"];
  237. if (curUrlNode.ChildNodes.IsNullOrEmpty())
  238. {
  239. #region The wildcard child has no more childs to verify, so lets take it
  240. parameters.Add(url.Substring(posInUrl));
  241. retval = new Tuple<UrlNode, List<object>>(curUrlNode, newParams); //parameters);
  242. #endregion
  243. }
  244. else
  245. {
  246. #region The wildcard child have mor childs which needs to be verified
  247. urlParts = urlParts.Skip(1);
  248. if (GetCallback(curUrlNode.ChildNodes, urlParts.Skip(1), newParams, url, posInUrl) == null)
  249. {
  250. #region The next parts do not leed into a successfull mapping, lets use this wildcard
  251. parameters.Add(url.Substring(posInUrl));
  252. retval = new Tuple<UrlNode, List<object>>(curUrlNode, parameters);
  253. parameters = null;
  254. #endregion
  255. }
  256. else
  257. {
  258. #region Take this wildcard as parameter and proceed
  259. val = urlParts.First();
  260. newParams.Add(url.Substring(posInUrl, (val.Length == 0) ? 1 : val.Length));
  261. posInUrl += (val.Length == 0) ? 1 : val.Length + 1;
  262. #endregion
  263. }
  264. #endregion
  265. }
  266. #endregion
  267. #endregion
  268. #endregion
  269. } while (retval == null);
  270. #region Are there any parameters to add to the result?
  271. if (!(parameters == null || parameters.Count == 0))
  272. {
  273. #region We need to swap the parameters due to recursive call
  274. parameters.AddRange(retval.Item2);
  275. retval = new Tuple<UrlNode, List<object>>(retval.Item1, parameters);
  276. #endregion
  277. }
  278. #endregion
  279. return retval;
  280. #endregion
  281. }
  282. if (curUrlNode.Callback == null && !curUrlNode.ChildNodes.IsNullOrEmpty() && curUrlNode.ChildNodes.ContainsKey("{}"))
  283. {
  284. #region The current callback is null AND this is the last part of the url
  285. if (posInUrl >= url.Length)
  286. return null;
  287. parameters.Add(url.Substring(posInUrl));
  288. curUrlNode = curUrlNode.ChildNodes["{}"];
  289. #endregion
  290. }
  291. else if (urlParts.CountIsGreater(1) && curUrlNode.ChildNodes.IsNullOrEmpty())
  292. {
  293. #region No childs but still some url parts
  294. return null;
  295. #endregion
  296. }
  297. else if (curUrlNode.Callback == null && !curUrlNode.ChildNodes.IsNullOrEmpty() && !curUrlNode.ChildNodes.ContainsKey("{}"))
  298. {
  299. #region There are childs but they have no placeholders ({}), so we have no valid definition
  300. return null;
  301. #endregion
  302. }
  303. return new Tuple<UrlNode, List<object>>(curUrlNode, parameters);
  304. }
  305. #endregion
  306. }
  307. }