/// <summary> Static class is used to read the configuration file defining microservice endpoints </summary> /// <param name="ConfigFiles"> Path and name of the configuration XML file to read </param> /// <returns> Fully configured microservices configuration object </returns> public static Microservice_Server_Configuration Read_Config(string[] ConfigFiles) { Microservice_Server_Configuration returnValue = new Microservice_Server_Configuration(); // Collections used to hold the objects read from the XML before they are all connected (due to references // within the XML file to other portions ) Dictionary<string, Microservice_Component> components = new Dictionary<string, Microservice_Component>(); Dictionary<string, Microservice_RestrictionRange> ranges = new Dictionary<string, Microservice_RestrictionRange>(); List<Microservice_Endpoint> allEndpoints = new List<Microservice_Endpoint>(); Dictionary<Microservice_VerbMapping, string> endpointToComponentDictionary = new Dictionary<Microservice_VerbMapping, string>(); Dictionary<Microservice_VerbMapping, string> endpointToRestrictionDictionary = new Dictionary<Microservice_VerbMapping, string>(); // Read these files foreach (string configFile in ConfigFiles) { read_engine_file(configFile, returnValue, allEndpoints, endpointToComponentDictionary, endpointToRestrictionDictionary, components, ranges); } // Now that everything has been read here, connect the objects together foreach (Microservice_Endpoint endpoint in allEndpoints) { // Step through each applicable verb --> method mapping ( i.e., GET, POST, PUT, and DELETE ) foreach (Microservice_VerbMapping verbmapping in endpoint.AllVerbMappings) { // Connect the component to the endpoint string componentid = endpointToComponentDictionary[verbmapping]; if (components.ContainsKey(componentid)) { verbmapping.Component = components[componentid]; } // Connect the restriction ranges to the endpoints, if not already done if (endpointToRestrictionDictionary.ContainsKey(verbmapping)) { string restrictionid = endpointToRestrictionDictionary[verbmapping]; string[] restrictions = restrictionid.Split(" ".ToCharArray()); foreach (string thisRestriction in restrictions) { if (ranges.ContainsKey(thisRestriction)) { Microservice_RestrictionRange rangeObj = ranges[thisRestriction]; if (verbmapping.RestrictionRanges == null) verbmapping.RestrictionRanges = new List<Microservice_RestrictionRange>(); verbmapping.RestrictionRanges.Add(rangeObj); } } } } } return returnValue; }
private static void read_microservices_details_restrictionranges(XmlReader ReaderXml, Microservice_Server_Configuration Config, Dictionary<string, Microservice_RestrictionRange> Ranges) { Microservice_RestrictionRange currentRange = null; while (ReaderXml.Read()) { if (ReaderXml.NodeType == XmlNodeType.Element) { switch (ReaderXml.Name.ToLower()) { case "range": string rangeId = null; if (ReaderXml.MoveToAttribute("ID")) rangeId = ReaderXml.Value.Trim(); // Must have an ID to be valid if (!String.IsNullOrEmpty(rangeId)) { currentRange = null; // Look for a matching range foreach (Microservice_RestrictionRange range in Config.RestrictionRanges) { if (range.ID == rangeId) { currentRange = range; break; } } // If no range, create the new one if (currentRange == null) { currentRange = new Microservice_RestrictionRange {ID = rangeId}; } if (ReaderXml.MoveToAttribute("Label")) currentRange.Label = ReaderXml.Value.Trim(); } else { // Missing ID in this range currentRange = null; } break; case "removeall": if (currentRange != null) currentRange.IpRanges.Clear(); break; case "iprange": if (currentRange != null) { Microservice_IpRange singleIpRange = new Microservice_IpRange(); if (ReaderXml.MoveToAttribute("Label")) singleIpRange.Label = ReaderXml.Value.Trim(); if (ReaderXml.MoveToAttribute("Start")) singleIpRange.StartIp = ReaderXml.Value.Trim(); if (ReaderXml.MoveToAttribute("End")) singleIpRange.EndIp = ReaderXml.Value.Trim(); if (singleIpRange.StartIp.Length > 0) currentRange.IpRanges.Add(singleIpRange); } break; } } else if (ReaderXml.NodeType == XmlNodeType.EndElement) { if (String.Compare(ReaderXml.Name, "range", StringComparison.OrdinalIgnoreCase) == 0) { if ((currentRange != null) && (!String.IsNullOrEmpty(currentRange.ID))) { Ranges[currentRange.ID] = currentRange; if (!Config.RestrictionRanges.Contains(currentRange)) Config.RestrictionRanges.Add(currentRange); } currentRange = null; } } } }
private static void read_microservices_details_mapping(XmlReader ReaderXml, Microservice_Server_Configuration Config, List<Microservice_Endpoint> AllEndpoints, Dictionary<Microservice_VerbMapping, string> EndpointToComponentDictionary, Dictionary<Microservice_VerbMapping, string> EndpointToRestrictionDictionary, Microservice_Path ParentSegment) { while (ReaderXml.Read()) { if (ReaderXml.NodeType == XmlNodeType.Element) { switch (ReaderXml.Name.ToLower()) { case "removeall": if (ParentSegment != null) { if ( ParentSegment.Children != null ) ParentSegment.Children.Clear(); } else { Config.RootPaths.Clear(); } break; case "path": if (ReaderXml.MoveToAttribute("Segment")) { Microservice_Path path; string segment = ReaderXml.Value.Trim(); if (ParentSegment == null) { if (Config.RootPaths.ContainsKey(segment.ToLower())) path = Config.RootPaths[segment.ToLower()]; else { path = new Microservice_Path { Segment = segment }; Config.RootPaths[segment.ToLower()] = path; } } else { if (ParentSegment.Children == null) ParentSegment.Children = new Dictionary<string, Microservice_Path>(StringComparer.OrdinalIgnoreCase); if (ParentSegment.Children.ContainsKey(segment.ToLower())) { path = ParentSegment.Children[segment.ToLower()]; } else { path = new Microservice_Path {Segment = segment}; ParentSegment.Children[path.Segment] = path; } } ReaderXml.MoveToElement(); XmlReader subTreeReader = ReaderXml.ReadSubtree(); subTreeReader.Read(); read_microservices_details_mapping(subTreeReader, Config, AllEndpoints, EndpointToComponentDictionary, EndpointToRestrictionDictionary, path); } break; case "complexendpoint": // Read the top-endpoint information, before getting to each verb mapping bool disabled_at_top = false; Microservice_Endpoint endpoint = new Microservice_Endpoint(); if (ReaderXml.MoveToAttribute("Segment")) endpoint.Segment = ReaderXml.Value.Trim(); if ((ReaderXml.MoveToAttribute("Enabled")) && (String.Compare(ReaderXml.Value.Trim(), "false", StringComparison.OrdinalIgnoreCase) == 0)) disabled_at_top = true; // Now, read what remains ReaderXml.MoveToElement(); XmlReader complexReader = ReaderXml.ReadSubtree(); complexReader.Read(); read_microservices_complex_endpoint_details(complexReader, EndpointToComponentDictionary, EndpointToRestrictionDictionary, endpoint, disabled_at_top ); // If a verb was mapped and there was a valid segment, add this if ((!String.IsNullOrEmpty(endpoint.Segment)) && (endpoint.HasVerbMapping)) { if (ParentSegment != null) { // Add this endpoint if (ParentSegment.Children == null) ParentSegment.Children = new Dictionary<string, Microservice_Path>(); ParentSegment.Children[endpoint.Segment] = endpoint; AllEndpoints.Add(endpoint); } } break; case "endpoint": read_microservices_simple_endpoint_details(ReaderXml, AllEndpoints, EndpointToComponentDictionary, EndpointToRestrictionDictionary, ParentSegment); break; } } } }
private static void read_microservices_details_components(XmlReader ReaderXml, Microservice_Server_Configuration Config, Dictionary<string, Microservice_Component> Components) { while (ReaderXml.Read()) { if (ReaderXml.NodeType == XmlNodeType.Element) { switch (ReaderXml.Name.ToLower()) { case "component": Microservice_Component component = new Microservice_Component(); if (ReaderXml.MoveToAttribute("ID")) component.ID = ReaderXml.Value.Trim(); if (ReaderXml.MoveToAttribute("Assembly")) component.Assembly = ReaderXml.Value.Trim(); if (ReaderXml.MoveToAttribute("Namespace")) component.Namespace = ReaderXml.Value.Trim(); if (ReaderXml.MoveToAttribute("Class")) component.Class = ReaderXml.Value.Trim(); if ((!String.IsNullOrEmpty(component.ID)) && (!String.IsNullOrEmpty(component.Class))) { // If the key already existed, remove the old one as it will be replaced if (Components.ContainsKey(component.ID)) { Config.Components.Remove(Components[component.ID]); Components.Remove(component.ID); } // Add this new component Components[component.ID] = component; Config.Components.Add(component); } break; } } } }
private static void read_engine_file(string ConfigFile, Microservice_Server_Configuration Config, List<Microservice_Endpoint> AllEndpoints, Dictionary<Microservice_VerbMapping, string> EndpointToComponentDictionary, Dictionary<Microservice_VerbMapping, string> EndpointToRestrictionDictionary, Dictionary<string, Microservice_Component> Components, Dictionary<string, Microservice_RestrictionRange> Ranges) { // Streams used for reading Stream readerStream = null; XmlTextReader readerXml = null; try { // Open a link to the file readerStream = new FileStream(ConfigFile, FileMode.Open, FileAccess.Read); // Open a XML reader connected to the file readerXml = new XmlTextReader(readerStream); while (readerXml.Read()) { if (readerXml.NodeType == XmlNodeType.Element) { switch (readerXml.Name.ToLower()) { case "engine": if (readerXml.MoveToAttribute("ClearAll")) { if (readerXml.Value.Trim().ToLower() == "true") { Config.ClearAll(); AllEndpoints.Clear(); Components.Clear(); Ranges.Clear(); EndpointToComponentDictionary.Clear(); EndpointToRestrictionDictionary.Clear(); } readerXml.MoveToElement(); } read_engine_details(readerXml.ReadSubtree(), Config, AllEndpoints, EndpointToComponentDictionary, EndpointToRestrictionDictionary, Components, Ranges); break; } } } } catch (Exception ee) { Config.Error = ee.Message; } finally { if (readerXml != null) { readerXml.Close(); } if (readerStream != null) { readerStream.Close(); } } }
private static void read_engine_details(XmlReader ReaderXml, Microservice_Server_Configuration Config, List<Microservice_Endpoint> AllEndpoints, Dictionary<Microservice_VerbMapping, string> EndpointToComponentDictionary, Dictionary<Microservice_VerbMapping, string> EndpointToRestrictionDictionary, Dictionary<string, Microservice_Component> Components, Dictionary<string, Microservice_RestrictionRange> Ranges) { // Just step through the subtree of this while (ReaderXml.Read()) { if (ReaderXml.NodeType == XmlNodeType.Element) { switch (ReaderXml.Name.ToLower()) { case "mapping": read_microservices_details_mapping(ReaderXml.ReadSubtree(), Config, AllEndpoints, EndpointToComponentDictionary, EndpointToRestrictionDictionary, null); break; case "components": read_microservices_details_components(ReaderXml.ReadSubtree(), Config, Components); break; case "restrictionranges": read_microservices_details_restrictionranges(ReaderXml.ReadSubtree(), Config, Ranges); break; } } } }
/// <summary> Processes the request </summary> /// <param name="Context">The context for the current request </param> public void ProcessRequest(HttpContext Context) { Engine_Database.Connection_String = Engine_ApplicationCache_Gateway.Settings.Database_Connections[0].Connection_String; // Get the original query string string queryString = Context.Request.QueryString["urlrelative"]; if (!String.IsNullOrEmpty(queryString)) { // Make sure the microservices configuration has been read if (microserviceConfig == null) { string default_path = Context.Server.MapPath("config/default/sobekcm_engine.config"); string user_path = Context.Server.MapPath("config/user/sobekcm_engine.config"); if (File.Exists(user_path)) { string[] config_paths = {default_path, user_path}; microserviceConfig = Microservice_Server_Config_Reader.Read_Config(config_paths); } else { microserviceConfig = Microservice_Server_Config_Reader.Read_Config(default_path); } } // Collect the requested paths string[] splitter; if (( queryString.IndexOf("/") == 0 ) && ( queryString.Length > 1 )) splitter = queryString.Substring(1).Split("/".ToCharArray()); else splitter = queryString.Split("/".ToCharArray()); List<string> paths = splitter.ToList(); // Set the encoding Context.Response.Charset = Encoding.UTF8.WebName; // Set this to allow us to have our own error messages, without IIS jumping into it Context.Response.TrySkipIisCustomErrors = true; // Get any matching endpoint configuration Microservice_Endpoint endpoint = microserviceConfig.Get_Endpoint(paths); if (endpoint == null) { Context.Response.ContentType = "text/plain"; Context.Response.StatusCode = 501; Context.Response.Write("No endpoint found"); } else { string method = Context.Request.HttpMethod.ToUpper(); if (!endpoint.VerbMappingExists(method)) { Context.Response.ContentType = "text/plain"; Context.Response.StatusCode = 406; Context.Response.Write("HTTP method " + method + " is not supported by this URL"); return; } // Get the specific verb mapping Microservice_VerbMapping verbMapping = endpoint[method]; // Ensure this is allowed in the range string requestIp = Context.Request.UserHostAddress; if (!verbMapping.AccessPermitted(requestIp)) { Context.Response.ContentType = "text/plain"; Context.Response.StatusCode = 403; Context.Response.Write("You are forbidden from accessing this endpoint"); return; } // Set the protocoal switch (verbMapping.Protocol) { case Microservice_Endpoint_Protocol_Enum.JSON: Context.Response.ContentType = "application/json"; break; case Microservice_Endpoint_Protocol_Enum.JSON_P: Context.Response.ContentType = "application/javascript"; break; case Microservice_Endpoint_Protocol_Enum.PROTOBUF: Context.Response.ContentType = "application/octet-stream"; break; case Microservice_Endpoint_Protocol_Enum.XML: Context.Response.ContentType = "text/xml"; break; case Microservice_Endpoint_Protocol_Enum.SOAP: Context.Response.ContentType = "text/xml"; break; case Microservice_Endpoint_Protocol_Enum.BINARY: Context.Response.ContentType = "application/octet-stream"; break; } // Determine if this is currently in a valid DEBUG mode bool debug = (Context.Request.QueryString["debug"] == "debug"); try { verbMapping.Invoke(Context.Response, paths, Context.Request.QueryString, Context.Request.Form, debug); } catch (Exception ee) { Context.Response.ContentType = "text/plain"; Context.Response.Output.WriteLine("Error invoking the exception method: " + ee.Message); Context.Response.StatusCode = 500; } } } else { Context.Response.ContentType = "text/plain"; Context.Response.StatusCode = 400; Context.Response.Write("Invalid URI - No endpoint requested"); } }