/// <summary> /// Creates peers for benchmarking. The first peer will be used as the master. /// This means that shutting down the master will shut down all other peers as well. /// </summary> /// <param name="nrOfPeers">Number of peers to create.</param> /// <param name="rnd">The random object used for peer ID creation.</param> /// <param name="port">The UDP and TCP port.</param> /// <param name="maintenance">Indicates whether maintenance should be enabled.</param> /// <param name="timeout">Indicates whether timeout should be enabled.</param> /// <returns></returns> public static Peer[] CreateNodes(int nrOfPeers, InteropRandom rnd, int port, bool maintenance, bool timeout) { Console.WriteLine("Creating network with {0} peers...", nrOfPeers); var peers = new Peer[nrOfPeers]; var masterId = CreateRandomId(rnd); var masterMap = new PeerMap(new PeerMapConfiguration(masterId)); var pb = new PeerBuilder(masterId) .SetPorts(port) .SetEnableMaintenance(maintenance) .SetExternalBindings(new Bindings()) .SetPeerMap(masterMap); if (!timeout) { pb.SetChannelServerConfiguration(CreateInfiniteTimeoutChannelServerConfiguration(port)); } peers[0] = pb.Start(); //Logger.Info("Created master peer: {0}.", peers[0].PeerId); for (int i = 1; i < nrOfPeers; i++) { peers[i] = CreateSlave(peers[0], rnd, maintenance, timeout); } return peers; }
/// <summary> /// Creates peers for benchmarking. The first peer will be used as the master. /// This means that shutting down the master will shut down all other peers as well. /// </summary> /// <param name="nrOfPeers">Number of peers to create.</param> /// <param name="rnd">The random object used for peer ID creation.</param> /// <param name="port">The UDP and TCP port.</param> /// <param name="maintenance">Indicates whether maintenance should be enabled.</param> /// <param name="timeout">Indicates whether timeout should be enabled.</param> /// <returns></returns> public static Peer[] CreateNodes(int nrOfPeers, InteropRandom rnd, int port, bool maintenance, bool timeout) { Console.WriteLine("Creating network with {0} peers...", nrOfPeers); var peers = new Peer[nrOfPeers]; var masterId = CreateRandomId(rnd); var masterMap = new PeerMap(new PeerMapConfiguration(masterId)); var pb = new PeerBuilder(masterId) .SetPorts(port) .SetEnableMaintenance(maintenance) .SetExternalBindings(new Bindings()) .SetPeerMap(masterMap); if (!timeout) { pb.SetChannelServerConfiguration(CreateInfiniteTimeoutChannelServerConfiguration(port)); } peers[0] = pb.Start(); //Logger.Info("Created master peer: {0}.", peers[0].PeerId); for (int i = 1; i < nrOfPeers; i++) { peers[i] = CreateSlave(peers[0], rnd, maintenance, timeout); } return(peers); }
/// <summary> /// Creates peers for testing. The first peer will be used as the master. /// This means that shutting down the master will shut down all other peers as well. /// </summary> /// <param name="nrOfPeers"></param> /// <param name="rnd"></param> /// <param name="port"></param> /// <param name="automaticTask"></param> /// <param name="maintenance"></param> /// <returns></returns> public static Peer[] CreateNodes(int nrOfPeers, Random rnd, int port, IAutomaticTask automaticTask, bool maintenance) { var bindings = new Bindings(); var peers = new Peer[nrOfPeers]; if (automaticTask != null) { var peerId = new Number160(rnd); var peerMap = new PeerMap(new PeerMapConfiguration(peerId)); peers[0] = new PeerBuilder(peerId) .SetPorts(port) .SetEnableMaintenance(maintenance) .SetExternalBindings(bindings) .SetPeerMap(peerMap) .Start() .AddAutomaticTask(automaticTask); } else { var peerId = new Number160(rnd); var peerMap = new PeerMap(new PeerMapConfiguration(peerId)); peers[0] = new PeerBuilder(peerId) .SetPorts(port) .SetEnableMaintenance(maintenance) .SetExternalBindings(bindings) .SetPeerMap(peerMap) .Start(); } Console.WriteLine("Created master peer: {0}.", peers[0].PeerId); for (int i = 1; i < nrOfPeers; i++) { if (automaticTask != null) { var peerId = new Number160(rnd); var peerMap = new PeerMap(new PeerMapConfiguration(peerId)); peers[i] = new PeerBuilder(peerId) .SetMasterPeer(peers[0]) .SetEnableMaintenance(maintenance) .SetExternalBindings(bindings) .SetPeerMap(peerMap) .Start() .AddAutomaticTask(automaticTask); } else { var peerId = new Number160(rnd); var peerMap = new PeerMap(new PeerMapConfiguration(peerId).SetPeerNoVerification()); peers[i] = new PeerBuilder(peerId) .SetMasterPeer(peers[0]) .SetEnableMaintenance(maintenance) .SetExternalBindings(bindings) .SetPeerMap(peerMap) .Start(); } Console.WriteLine("Created slave peer {0}: {1}.", i, peers[i].PeerId); } return(peers); }
private static Peer CreateSlave(Peer master, InteropRandom rnd, bool maintenance, bool timeout) { var slaveId = CreateRandomId(rnd); var slaveMap = new PeerMap(new PeerMapConfiguration(slaveId).SetPeerNoVerification()); var pb = new PeerBuilder(slaveId) .SetMasterPeer(master) .SetEnableMaintenance(maintenance) .SetExternalBindings(new Bindings()) .SetPeerMap(slaveMap); if (!timeout) { pb.SetChannelServerConfiguration(CreateInfiniteTimeoutChannelServerConfiguration(Ports.DefaultPort)); } var slave = pb.Start(); //Logger.Info("Created slave peer {0}.", slave.PeerId); return slave; }
private static Peer CreateSlave(Peer master, InteropRandom rnd, bool maintenance, bool timeout) { var slaveId = CreateRandomId(rnd); var slaveMap = new PeerMap(new PeerMapConfiguration(slaveId).SetPeerNoVerification()); var pb = new PeerBuilder(slaveId) .SetMasterPeer(master) .SetEnableMaintenance(maintenance) .SetExternalBindings(new Bindings()) .SetPeerMap(slaveMap); if (!timeout) { pb.SetChannelServerConfiguration(CreateInfiniteTimeoutChannelServerConfiguration(Ports.DefaultPort)); } var slave = pb.Start(); //Logger.Info("Created slave peer {0}.", slave.PeerId); return(slave); }
public PeerBuilder SetPeerMap(PeerMap peerMap) { PeerMap = peerMap; return this; }
/// <summary> /// Creates a peer and starts to listen for incoming connections. /// </summary> /// <returns>The peer that can operate in the P2P network.</returns> public Peer Start() { if (_behindFirewall == null) { _behindFirewall = false; } if (ChannelServerConfiguration == null) { ChannelServerConfiguration = CreateDefaultChannelServerConfiguration(); ChannelServerConfiguration.SetPortsForwarding(new Ports(TcpPortForwarding, UdpPortForwarding)); if (TcpPort == -1) { TcpPort = Ports.DefaultPort; } if (UdpPort == -1) { UdpPort = Ports.DefaultPort; } ChannelServerConfiguration.SetPorts(new Ports(TcpPort, UdpPort)); ChannelServerConfiguration.SetIsBehindFirewall(_behindFirewall.Value); } if (ChannelClientConfiguration == null) { ChannelClientConfiguration = CreateDefaultChannelClientConfiguration(); } if (KeyPair == null) { KeyPair = EmptyKeyPair; } if (P2PId == -1) { P2PId = 1; } if (InterfaceBindings == null) { InterfaceBindings = new Bindings(); } ChannelServerConfiguration.SetBindingsIncoming(InterfaceBindings); if (ExternalBindings == null) { ExternalBindings = new Bindings(); } ChannelClientConfiguration.SetBindingsOutgoing(ExternalBindings); if (PeerMap == null) { PeerMap = new PeerMap(new PeerMapConfiguration(PeerId)); } if (MasterPeer == null && Timer == null) { Timer = new ExecutorService(); } PeerCreator peerCreator; if (MasterPeer != null) { // create slave peer peerCreator = new PeerCreator(MasterPeer.PeerCreator, PeerId, KeyPair); } else { // create master peer peerCreator = new PeerCreator(P2PId, PeerId, KeyPair, ChannelServerConfiguration, ChannelClientConfiguration, Timer); } var peer = new Peer(P2PId, PeerId, peerCreator); var peerBean = peerCreator.PeerBean; peerBean.AddPeerStatusListener(PeerMap); var connectionBean = peerCreator.ConnectionBean; peerBean.SetPeerMap(PeerMap); peerBean.SetKeyPair(KeyPair); if (BloomfilterFactory == null) { peerBean.SetBloomfilterFactory(new DefaultBloomFilterFactory()); } if (BroadcastHandler == null) { BroadcastHandler = new DefaultBroadcastHandler(peer, new Random()); } // set/enable RPC if (IsEnabledHandshakeRpc) { var pingRpc = new PingRpc(peerBean, connectionBean); peer.SetPingRpc(pingRpc); } if (IsEnabledQuitRpc) { var quitRpc = new QuitRpc(peerBean, connectionBean); quitRpc.AddPeerStatusListener(PeerMap); peer.SetQuitRpc(quitRpc); } if (IsEnabledNeighborRpc) { var neighborRpc = new NeighborRpc(peerBean, connectionBean); peer.SetNeighborRpc(neighborRpc); } if (IsEnabledDirectDataRpc) { var directDataRpc = new DirectDataRpc(peerBean, connectionBean); peer.SetDirectDataRpc(directDataRpc); } if (IsEnabledBroadcastRpc) { var broadcastRpc = new BroadcastRpc(peerBean, connectionBean, BroadcastHandler); peer.SetBroadcastRpc(broadcastRpc); } if (IsEnabledRoutingRpc && IsEnabledNeighborRpc) { var routing = new DistributedRouting(peerBean, peer.NeighborRpc); peer.SetDistributedRouting(routing); } if (MaintenanceTask == null && IsEnabledMaintenance) { MaintenanceTask = new MaintenanceTask(); } if (MaintenanceTask != null) { MaintenanceTask.Init(peer, connectionBean.Timer); MaintenanceTask.AddMaintainable(PeerMap); } peerBean.SetMaintenanceTask(MaintenanceTask); // set the ping builder for the heart beat connectionBean.Sender.SetPingBuilderFactory(new PingBuilderFactory(peer)); foreach (var peerInit in _toInitialize) { peerInit.Init(peer); } return peer; }
/// <summary> /// Sets a new map storing neighbors for this peer. /// </summary> /// <param name="peerMap">The new map that stores neighbors.</param> /// <returns>This class.</returns> public PeerBean SetPeerMap(PeerMap peerMap) { PeerMap = peerMap; return(this); }
private static void Configure (Stream input) { XmlDocument doc = new XmlDocument (); doc.Load (input); // assemblies assembly_paths = new ArrayList (); assembly_paths.Add ("."); foreach (XmlNode node in doc.SelectNodes ("config/assemblypath")) { assembly_paths.Add (node.Attributes["path"].Value); } assemblies = new ArrayList (); foreach (XmlNode node in doc.SelectNodes ("config/assembly")) { assemblies.Add (node.Attributes["file"].Value); } LoadAssemblies (); // outputfiles output_path = "."; XmlNode path_node = doc.SelectSingleNode ("config/outputpath"); if (path_node != null) output_path = path_node.Attributes["path"].Value; output_files = new Hashtable (); output_includes = new Hashtable (); foreach (XmlNode node in doc.SelectNodes ("config/outputfile")) { string name = node.Attributes["name"].Value; output_files.Add (name, node.Attributes["file"].Value); foreach (XmlNode child in node.ChildNodes) { if (child.Name == "include") output_includes[name] = child.InnerText; } } // typemap peer_map = new PeerMap (); foreach (XmlNode node in doc.SelectNodes ("config/typemap/namespace")) { string ns = node.Attributes["name"].Value; foreach (XmlNode child in node.ChildNodes) { if (child.Name == "type") { string name = child.Attributes["name"].Value; string peer_name = child.Attributes["peer"].Value; bool opaque = false; if (child.Attributes["opaque"] != null && child.Attributes["opaque"].Value == "true") opaque = true; String fullname = String.Format ("{0}.{1}", ns, name); Type type; if (child.Attributes["default"] != null && child.Attributes["default"].Value == "true") type = Type.GetType (fullname); else type = (Type)types [fullname]; if (type != null) peer_map.Add (new Peer (type, peer_name, opaque)); } } } peer_map.ResolvePeers (); }
public Statistics(PeerMap peerMap) { _peerMap = peerMap; AvgGap = Max / 2; }
/// <summary> /// Sets a new map storing neighbors for this peer. /// </summary> /// <param name="peerMap">The new map that stores neighbors.</param> /// <returns>This class.</returns> public PeerBean SetPeerMap(PeerMap peerMap) { PeerMap = peerMap; return this; }
public PeerBuilder SetPeerMap(PeerMap peerMap) { PeerMap = peerMap; return(this); }
/// <summary> /// Creates a peer and starts to listen for incoming connections. /// </summary> /// <returns>The peer that can operate in the P2P network.</returns> public Peer Start() { if (_behindFirewall == null) { _behindFirewall = false; } if (ChannelServerConfiguration == null) { ChannelServerConfiguration = CreateDefaultChannelServerConfiguration(); ChannelServerConfiguration.SetPortsForwarding(new Ports(TcpPortForwarding, UdpPortForwarding)); if (TcpPort == -1) { TcpPort = Ports.DefaultPort; } if (UdpPort == -1) { UdpPort = Ports.DefaultPort; } ChannelServerConfiguration.SetPorts(new Ports(TcpPort, UdpPort)); ChannelServerConfiguration.SetIsBehindFirewall(_behindFirewall.Value); } if (ChannelClientConfiguration == null) { ChannelClientConfiguration = CreateDefaultChannelClientConfiguration(); } if (KeyPair == null) { KeyPair = EmptyKeyPair; } if (P2PId == -1) { P2PId = 1; } if (InterfaceBindings == null) { InterfaceBindings = new Bindings(); } ChannelServerConfiguration.SetBindingsIncoming(InterfaceBindings); if (ExternalBindings == null) { ExternalBindings = new Bindings(); } ChannelClientConfiguration.SetBindingsOutgoing(ExternalBindings); if (PeerMap == null) { PeerMap = new PeerMap(new PeerMapConfiguration(PeerId)); } if (MasterPeer == null && Timer == null) { Timer = new ExecutorService(); } PeerCreator peerCreator; if (MasterPeer != null) { // create slave peer peerCreator = new PeerCreator(MasterPeer.PeerCreator, PeerId, KeyPair); } else { // create master peer peerCreator = new PeerCreator(P2PId, PeerId, KeyPair, ChannelServerConfiguration, ChannelClientConfiguration, Timer); } var peer = new Peer(P2PId, PeerId, peerCreator); var peerBean = peerCreator.PeerBean; peerBean.AddPeerStatusListener(PeerMap); var connectionBean = peerCreator.ConnectionBean; peerBean.SetPeerMap(PeerMap); peerBean.SetKeyPair(KeyPair); if (BloomfilterFactory == null) { peerBean.SetBloomfilterFactory(new DefaultBloomFilterFactory()); } if (BroadcastHandler == null) { BroadcastHandler = new DefaultBroadcastHandler(peer, new Random()); } // set/enable RPC if (IsEnabledHandshakeRpc) { var pingRpc = new PingRpc(peerBean, connectionBean); peer.SetPingRpc(pingRpc); } if (IsEnabledQuitRpc) { var quitRpc = new QuitRpc(peerBean, connectionBean); quitRpc.AddPeerStatusListener(PeerMap); peer.SetQuitRpc(quitRpc); } if (IsEnabledNeighborRpc) { var neighborRpc = new NeighborRpc(peerBean, connectionBean); peer.SetNeighborRpc(neighborRpc); } if (IsEnabledDirectDataRpc) { var directDataRpc = new DirectDataRpc(peerBean, connectionBean); peer.SetDirectDataRpc(directDataRpc); } if (IsEnabledBroadcastRpc) { var broadcastRpc = new BroadcastRpc(peerBean, connectionBean, BroadcastHandler); peer.SetBroadcastRpc(broadcastRpc); } if (IsEnabledRoutingRpc && IsEnabledNeighborRpc) { var routing = new DistributedRouting(peerBean, peer.NeighborRpc); peer.SetDistributedRouting(routing); } if (MaintenanceTask == null && IsEnabledMaintenance) { MaintenanceTask = new MaintenanceTask(); } if (MaintenanceTask != null) { MaintenanceTask.Init(peer, connectionBean.Timer); MaintenanceTask.AddMaintainable(PeerMap); } peerBean.SetMaintenanceTask(MaintenanceTask); // set the ping builder for the heart beat connectionBean.Sender.SetPingBuilderFactory(new PingBuilderFactory(peer)); foreach (var peerInit in _toInitialize) { peerInit.Init(peer); } return(peer); }
/// <summary> /// Creates peers for testing. The first peer will be used as the master. /// This means that shutting down the master will shut down all other peers as well. /// </summary> /// <param name="nrOfPeers"></param> /// <param name="rnd"></param> /// <param name="port"></param> /// <param name="automaticTask"></param> /// <param name="maintenance"></param> /// <returns></returns> public static Peer[] CreateNodes(int nrOfPeers, Random rnd, int port, IAutomaticTask automaticTask, bool maintenance) { var bindings = new Bindings(); var peers = new Peer[nrOfPeers]; if (automaticTask != null) { var peerId = new Number160(rnd); var peerMap = new PeerMap(new PeerMapConfiguration(peerId)); peers[0] = new PeerBuilder(peerId) .SetPorts(port) .SetEnableMaintenance(maintenance) .SetExternalBindings(bindings) .SetPeerMap(peerMap) .Start() .AddAutomaticTask(automaticTask); } else { var peerId = new Number160(rnd); var peerMap = new PeerMap(new PeerMapConfiguration(peerId)); peers[0] = new PeerBuilder(peerId) .SetPorts(port) .SetEnableMaintenance(maintenance) .SetExternalBindings(bindings) .SetPeerMap(peerMap) .Start(); } Console.WriteLine("Created master peer: {0}.", peers[0].PeerId); for (int i = 1; i < nrOfPeers; i++) { if (automaticTask != null) { var peerId = new Number160(rnd); var peerMap = new PeerMap(new PeerMapConfiguration(peerId)); peers[i] = new PeerBuilder(peerId) .SetMasterPeer(peers[0]) .SetEnableMaintenance(maintenance) .SetExternalBindings(bindings) .SetPeerMap(peerMap) .Start() .AddAutomaticTask(automaticTask); } else { var peerId = new Number160(rnd); var peerMap = new PeerMap(new PeerMapConfiguration(peerId).SetPeerNoVerification()); peers[i] = new PeerBuilder(peerId) .SetMasterPeer(peers[0]) .SetEnableMaintenance(maintenance) .SetExternalBindings(bindings) .SetPeerMap(peerMap) .Start(); } Console.WriteLine("Created slave peer {0}: {1}.", i, peers[i].PeerId); } return peers; }
private static void Configure(Stream input) { XmlDocument doc = new XmlDocument(); doc.Load(input); // assemblies assembly_paths = new ArrayList(); assembly_paths.Add("."); foreach (XmlNode node in doc.SelectNodes("config/assemblypath")) { assembly_paths.Add(node.Attributes["path"].Value); } assemblies = new ArrayList(); foreach (XmlNode node in doc.SelectNodes("config/assembly")) { assemblies.Add(node.Attributes["file"].Value); } LoadAssemblies(); // outputfiles output_path = "."; XmlNode path_node = doc.SelectSingleNode("config/outputpath"); if (path_node != null) { output_path = path_node.Attributes["path"].Value; } output_files = new Hashtable(); output_includes = new Hashtable(); foreach (XmlNode node in doc.SelectNodes("config/outputfile")) { string name = node.Attributes["name"].Value; output_files.Add(name, node.Attributes["file"].Value); foreach (XmlNode child in node.ChildNodes) { if (child.Name == "include") { output_includes[name] = child.InnerText; } } } // typemap peer_map = new PeerMap(); foreach (XmlNode node in doc.SelectNodes("config/typemap/namespace")) { string ns = node.Attributes["name"].Value; foreach (XmlNode child in node.ChildNodes) { if (child.Name == "type") { string name = child.Attributes["name"].Value; string peer_name = child.Attributes["peer"].Value; bool opaque = false; if (child.Attributes["opaque"] != null && child.Attributes["opaque"].Value == "true") { opaque = true; } String fullname = String.Format("{0}.{1}", ns, name); Type type; if (child.Attributes["default"] != null && child.Attributes["default"].Value == "true") { type = Type.GetType(fullname); } else { type = (Type)types [fullname]; } if (type != null) { peer_map.Add(new Peer(type, peer_name, opaque)); } } } } peer_map.ResolvePeers(); }
/// <summary> /// Looks for a route to the given peer address. /// </summary> /// <param name="peerAddresses">Nodes that should be asked first for a route.</param> /// <param name="routingBuilder"></param> /// <param name="type"></param> /// <param name="cc"></param> /// <returns>A task object that is set to complete if the route has been found.</returns> private TcsRouting Routing(ICollection <PeerAddress> peerAddresses, RoutingBuilder routingBuilder, Message.Message.MessageType type, ChannelCreator cc) { try { if (peerAddresses == null) { throw new ArgumentException("Some nodes/addresses need to be specified."); } bool randomSearch = routingBuilder.LocationKey == null; IComparer <PeerAddress> comparer; if (randomSearch) { comparer = _peerBean.PeerMap.CreateComparer(); } else { comparer = PeerMap.CreateComparer(routingBuilder.LocationKey); } var queueToAsk = new SortedSet <PeerAddress>(comparer); var alreadyAsked = new SortedSet <PeerAddress>(comparer); // As presented by Kazuyuki Shudo at AIMS 2009, it is better to ask random // peers with the data than ask peers that are ordered by distance. // -> this balances load var directHits = new SortedDictionary <PeerAddress, DigestInfo>(comparer); var potentialHits = new SortedSet <PeerAddress>(comparer); // fill initially queueToAsk.AddAll(peerAddresses); alreadyAsked.Add(_peerBean.ServerPeerAddress); potentialHits.Add(_peerBean.ServerPeerAddress); // domain key can be null if we bootstrap if (type == Message.Message.MessageType.Request2 && routingBuilder.DomainKey != null && !randomSearch && _peerBean.DigestStorage != null) { Number640 from; Number640 to; if (routingBuilder.From != null && routingBuilder.To != null) { from = routingBuilder.From; to = routingBuilder.To; } else if (routingBuilder.ContentKey == null) { from = new Number640(routingBuilder.LocationKey, routingBuilder.DomainKey, Number160.Zero, Number160.Zero); to = new Number640(routingBuilder.LocationKey, routingBuilder.DomainKey, Number160.MaxValue, Number160.MaxValue); } else { from = new Number640(routingBuilder.LocationKey, routingBuilder.DomainKey, routingBuilder.ContentKey, Number160.Zero); to = new Number640(routingBuilder.LocationKey, routingBuilder.DomainKey, routingBuilder.ContentKey, Number160.MaxValue); } var digestBean = _peerBean.DigestStorage.Digest(from, to, -1, true); if (digestBean.Size > 0) { directHits.Add(_peerBean.ServerPeerAddress, digestBean); } } else if (type == Message.Message.MessageType.Request3 && !randomSearch && _peerBean.DigestTracker != null) { var digestInfo = _peerBean.DigestTracker.Digest(routingBuilder.LocationKey, routingBuilder.DomainKey, routingBuilder.ContentKey); // we always put ourselfs to the tracker list, so we need to check // if we know also other peers on our trackers if (digestInfo.Size > 0) { directHits.Add(_peerBean.ServerPeerAddress, digestInfo); } } var tcsRouting = new TcsRouting(); if (peerAddresses.Count == 0) { tcsRouting.SetNeighbors(directHits, potentialHits, alreadyAsked, routingBuilder.IsBootstrap, false); } else { // If a peer bootstraps to itself, then the size of peer addresses is 1 // and it contains itself. Check for that because we need to know if we // are routing, bootstrapping and bootstrapping to ourselfs, to return // the correct status for the task. var isRoutingOnlyToSelf = peerAddresses.Count == 1 && peerAddresses.First().Equals(_peerBean.ServerPeerAddress); var routingMechanism = routingBuilder.CreateRoutingMechanism(tcsRouting); routingMechanism.SetQueueToAsk(queueToAsk); routingMechanism.SetPotentialHits(potentialHits); routingMechanism.SetDirectHits(directHits); routingMechanism.SetAlreadyAsked(alreadyAsked); routingBuilder.SetIsRoutingOnlyToSelf(isRoutingOnlyToSelf); RoutingRec(routingBuilder, routingMechanism, type, cc); } return(tcsRouting); } catch (Exception ex) { Logger.Error("An exception occurred during routing.", ex); throw; } }