/** * Prefered constructor for a Connection */ public Connection(Edge e, Address a, string connectiontype, StatusMessage sm, LinkMessage peerlm) { _e = e; _a = a; _ct = String.Intern(connectiontype); _stat = sm; _lm = peerlm; _creation_time = DateTime.UtcNow; MainType = StringToMainType(_ct); }
/** * Prefered constructor for a Connection */ public Connection(Edge e, Address a, string connectiontype, StatusMessage sm, LinkMessage peerlm) { _e = e; _a = a; _ct = String.Intern(connectiontype); _stat = sm; _lm = peerlm; _creation_time = DateTime.UtcNow; MainType = StringToMainType(_ct); _as_dict = new WriteOnce<ListDictionary>(); _sub_type = new WriteOnce<string>(); }
public CphState(Edge e, LinkMessage lm) { Edge = e; LM = lm; }
/** * This starts a linking operation on the given edge */ public IDictionary Start(IDictionary link_message, ISender edge) { if(ProtocolLog.LinkDebug.Enabled) ProtocolLog.Write(ProtocolLog.LinkDebug, String.Format( "{0} -start- sys:link.Start", _node.Address)); Edge from = GetEdge(edge); LinkMessage lm = new LinkMessage(link_message); if(ProtocolLog.LinkDebug.Enabled) ProtocolLog.Write(ProtocolLog.LinkDebug, String.Format( "{0} -args- sys:link.Start({1},{2})", _node.Address,lm,from)); CphState cph = new CphState(from,lm); lock( _sync ) { if( !_edge_to_cphstate.ContainsKey( from ) ) { _edge_to_cphstate[from] = cph; } else { throw new AdrException((int)ErrorMessage.ErrorCode.InProgress, "Already have a link in progress on this edge"); } } ErrorMessage err = null; if( CanConnect(cph, out err) ) { try { //If the CloseEvent was already called, this throws an exception from.CloseEvent += this.CloseHandler; } catch { CloseHandler(from, null); throw new AdrException((int)ErrorMessage.ErrorCode.EdgeClosed, "Edge Closed after receiving message"); } } else { lock( _sync ) { _edge_to_cphstate.Remove(from); } } //Now we prepare our response LinkMessage lm_resp = null; if( err == null ) { //We send a response: NodeInfo n_info = NodeInfo.CreateInstance( _node.Address, from.LocalTA ); NodeInfo remote_info = NodeInfo.CreateInstance( null, from.RemoteTA ); System.Collections.Specialized.StringDictionary attrs = new System.Collections.Specialized.StringDictionary(); attrs["type"] = String.Intern( lm.ConTypeString ); attrs["realm"] = String.Intern( _node.Realm ); lm_resp = new LinkMessage( attrs, n_info, remote_info , lm.Token); } else { if( err.Ec == ErrorMessage.ErrorCode.AlreadyConnected ) { /** * When we send the ErrorCode.AlreadyConnected, * we could have a stale connection, lets try pinging * the other node, if they are there, but have lost * the Edge, this may trigger the edge to close, causing * us to remove the Connection. * @todo consider putting this address on a "fast track" * to removal if we don't hear from it soon */ ConnectionTable tab = _node.ConnectionTable; Connection c = tab.GetConnection( lm.ConnectionType, lm.Local.Address ); if( c != null ) { RpcManager rpc = RpcManager.GetInstance(_node); rpc.Invoke(c.Edge, null, "sys:link.Ping", String.Empty); } } } if( err != null ) { throw new AdrException((int)err.Ec, err.Message); } if(ProtocolLog.LinkDebug.Enabled) ProtocolLog.Write(ProtocolLog.LinkDebug, String.Format( "{0} -end- sys:link.Start()->{1}", _node.Address,lm_resp)); return lm_resp.ToDictionary(); }
/** * When we get a response to the sys:link method, this handled * is called */ protected void LinkCloseHandler(object q, EventArgs args) { try { Channel resq = (Channel)q; //If the Channel is empty this will throw an exception: RpcResult res = (RpcResult)resq.Dequeue(); /* Here's the LinkMessage response */ LinkMessage lm = new LinkMessage( (IDictionary)res.Result ); /** * This will set our LinkMessageReply variable. It can * only be set once, so all future sets will fail. It * will also make sure we have the lock on the target. * If we don't, that will throw an exception */ SetAndCheckLinkReply(lm); //If we got here, we have our response and the Lock on _target_address StatusMessage sm = _node.GetStatus(_contype, lm.Local.Address); /* Make the call */ Channel results = new Channel(); results.CloseAfterEnqueue(); results.CloseEvent += this.StatusCloseHandler; RpcManager rpc = RpcManager.GetInstance(_node); if (ProtocolLog.LinkDebug.Enabled) { ProtocolLog.Write(ProtocolLog.LinkDebug, String.Format( "LPS target: {0} Invoking GetStatus() over edge: {1}", _linker.Target, _e)); } /* * This could throw an exception if the Edge is closed */ rpc.Invoke(_e, results, "sys:link.GetStatus", sm.ToDictionary() ); } catch(AdrException x) { /* * This happens when the RPC call has some kind of issue, * first we check for common error conditions: */ _x.Value = x; Finish( GetResultForErrorCode(x.Code) ); } catch(ConnectionExistsException x) { /* We already have a connection */ _x.Value = x; Finish( Result.ProtocolError ); } catch(CTLockException x) { //This is thrown when ConnectionTable cannot lock. Lets try again: _x.Value = x; Finish( Result.RetryThisTA ); } catch(LinkException x) { _x.Value = x; if( x.IsCritical ) { Finish( Result.MoveToNextTA ); } else { Finish( Result.RetryThisTA ); } } catch(InvalidOperationException) { //The queue never got anything Finish(Result.MoveToNextTA); } catch(EdgeException) { //The Edge is goofy, let's move on: Finish(Result.MoveToNextTA); } catch(Exception x) { //The protocol was not followed correctly by the other node, fail _x.Value = x; Finish( Result.RetryThisTA ); } }
/** * Checks that everything matches up and the protocol * can continue, throws and exception if anything is * not okay */ protected void SetAndCheckLinkReply(LinkMessage lm) { /* Check that the everything matches up * Make sure the link message is Kosher. * This are critical errors. This Link fails if these occur */ if( lm.Local == null) { throw new LinkException("Bad response"); } if( _node.Address.Equals( lm.Local.Address ) ) { //Somehow, we got a response from someone claiming to be us. throw new LinkException("Got a LinkMessage response from our address"); } if( lm.ConTypeString != _contype ) { throw new LinkException("Link type mismatch: " + _contype + " != " + lm.ConTypeString ); } if( !lm.Attributes["realm"].Equals( _node.Realm ) ) { throw new LinkException("Realm mismatch: " + _node.Realm + " != " + lm.Attributes["realm"] ); } if( lm.Local.Address == null ) { throw new LinkException("LinkMessage response has null Address"); } if( (_linker.Target != null) && (!lm.Local.Address.Equals( _linker.Target )) ) { /* * This is super goofy. Somehow we got a response from some node * we didn't mean to connect to. * This can happen in some cases with NATs since nodes behind NATs are * guessing which ports are correct, their guess may be incorrect, and * the NAT may send the packet to a different node. * In this case, we have a critical error, this TA is not correct, we * must move on to the next TA. */ throw new LinkException(String.Format("Target mismatch: {0} != {1}", _linker.Target, lm.Local.Address), true, null ); } /* * Okay, this lm looks good, we'll accept it. This can only be done * once, and once it happens a future attempt will throw an exception */ _lm_reply.Value = lm; ConnectionTable tab = _node.ConnectionTable; /* * This throws an exception if: * 0) we can't get the lock. * 1) we already have set _target_lock to something else */ tab.Lock( lm.Local.Address, _contype, this ); }
public void LMSerializationTest() { NodeInfo n1 = NodeInfo.CreateInstance(null, TransportAddressFactory.CreateInstance("brunet.tcp://127.0.0.1:45")); RandomNumberGenerator rng = new RNGCryptoServiceProvider(); AHAddress tmp_add = new AHAddress(rng); LinkMessage l1 = new LinkMessage(ConnectionType.Structured, n1, NodeInfo.CreateInstance(new DirectionalAddress(DirectionalAddress.Direction.Left), TransportAddressFactory.CreateInstance("brunet.tcp://127.0.0.1:837")), tmp_add.ToString() ); RoundTripHT(l1); StringDictionary attrs = new StringDictionary(); attrs["realm"] = "test_realm"; attrs["type"] = "structured.near"; LinkMessage l3 = new LinkMessage(attrs, n1, n1, tmp_add.ToString()); RoundTripHT(l3); }
public void RoundTripHT(LinkMessage lm) { LinkMessage lm2 = new LinkMessage( lm.ToDictionary() ); Assert.AreEqual( lm, lm2, "LinkMessage HT Roundtrip" ); }