int IMessageController.SendToPeer(SendMemoryToPeer message) { if (this.IsAlive) { return(CastTargetController.SendToPeer(message)); } else { return(0); } }
/// <summary> /// Closes the TCP connection as soon as possible. /// </summary> internal void Close(bool disposing) { // TODO: need to create an asymmetric close handshake SendMemoryToPeer endMsg = new SendMemoryToPeer(CLOSE_MESSAGE, new List<int>() { this.remote_uid }); ResponseCode(endMsg.MemBuffer, CLOSE_MESSAGE, GetListOfThisLocalID(), 0, 0, 200); SendToRemoteClient(endMsg); if (!disposing) { // spin wait while (this.state != ConnectionState.Closed) { Thread.Sleep(P2PDictionary.SLEEP_WAIT_TO_CLOSE); } } }
private void HandleReadDelete(string contentLocation,string eTag, List<int> senders, List<int> responsePath ) { // handle proxy messages if (contentLocation.StartsWith(PROXY_PREFIX + "/")) { contentLocation = contentLocation.Substring(PROXYPREFIX_REMOVE); if (!keysToListen.IsSubscribed(contentLocation)) { keysToListen.AddSubscription(contentLocation, SubscriptionInitiator.AutoProxyKey); } } // read ResponseAction status = ReadDelete(contentLocation, eTag, new List<int>(senders)); if (status == ResponseAction.ForwardToAll) { //conflict happened in data somewhere // return new data to sender senders.Clear(); } if (status != ResponseAction.DoNotForward) { // send a notification of deleted content immediately DataEntry entry = P2PDictionary.GetEntry(this.data, this.dataLock, contentLocation); System.Diagnostics.Debug.Assert(entry != null); // add to wire to send out senders.Add(this.local_uid);// add my sender to the packet SendBroadcastMemory sendMsg = new SendBroadcastMemory(entry.key, senders); WriteMethodDeleted(sendMsg.MemBuffer, contentLocation, senders, responsePath, entry.lastOwnerID, entry.lastOwnerRevision); controller.BroadcastToWire(sendMsg); } if (responsePath != null) { // well, i still have to send out this message because there is a path requested to follow DataEntry entry = P2PDictionary.GetEntry(this.data, this.dataLock, contentLocation); System.Diagnostics.Debug.Assert(entry != null); SendMemoryToPeer sendMsg = new SendMemoryToPeer(entry.key, responsePath); senders.Add(this.local_uid);// add my sender to the packet WriteMethodDeleted(sendMsg.MemBuffer, PROXY_PREFIX + contentLocation, senders, responsePath, entry.lastOwnerID, entry.lastOwnerRevision); } }
/// <summary> /// Creates a message with LocalUID as the sender list /// </summary> /// <param name="proxyKey">The resource name to return, prefixed with proxy/* for a proxy response.</param> /// <param name="key">Any dictionary element in the data/* namespace, or the data dictionary itself.</param> /// <returns></returns> public SendMemoryToPeer CreateResponseMessage(string key, string proxyKey, List<int> senderPath, List<int> includeList, List<int> proxyResponsePath) { SendMemoryToPeer msg = new SendMemoryToPeer(proxyKey, includeList); if (key == DATA_NAMESPACE) { ResponseDictionary(GET, msg.MemBuffer, senderPath, proxyResponsePath, false); } else { DataEntry entry = P2PDictionary.GetEntry( this.data, this.dataLock, key); System.Diagnostics.Debug.Assert(entry != null); if (!entry.subscribed) { throw new NotImplementedException(); } Response(GET, proxyKey, senderPath, proxyResponsePath, entry, msg.MemBuffer, false); } return msg; }
/// <summary> /// Fills bufferedOutput with the response, or asks the controller to pull the contentLocation from another peer. /// </summary> /// <param name="contentLocation">A location prefixed with /proxy.</param> /// <param name="requestPath">Path that the request should follow.</param> /// <returns>A new object to reply to the sender</returns> private SendMemoryToPeer RespondOrForwardProxy(string contentLocation, List<int> senderList) { // first 6 characters of /proxy are removed bool proxyPart = contentLocation.StartsWith(PROXY_PREFIX + "/"); if (proxyPart == false) throw new NotSupportedException("RespondOrForwardProxy no proxy part"); string key = contentLocation.Substring(PROXYPREFIX_REMOVE); List<int> hintPath = null; // cannnot proxy request the whole dictionary if (key == DATA_NAMESPACE) throw new NotSupportedException("RespondOrForwardProxy DATA_NAMESPACE"); bool responded = false; DataEntry e = P2PDictionary.GetEntry( this.data, this.dataLock, key); if (e != null) { WriteDebug(this.local_uid + " following proxy path, found content for " + key); lock (e) { if (e.subscribed && !DataMissing.IsSingleton(e.value)) { responded = true; } } if (responded) { // change the return path of the response message List<int> followList = new List<int>(senderList); followList.Remove(this.local_uid); SendMemoryToPeer sendMsg = CreateResponseMessage(key, PROXY_PREFIX + key, GetListOfThisLocalID(), followList, followList); return sendMsg; } hintPath = e.senderPath; } if (!responded) { // fix the requestPath with the hintPath if there is no requestPath, // or if the requestPath is now at the current peer if (hintPath == null || hintPath.Count == 0) { WriteDebug(this.local_uid + " forwarding request dropped " + key); } else { // since the path contains all the nodes to contact in order, // we don't have to broadcast a request. Instead, we just specify // the path to the next peer and it will get to the destination. senderList = new List<int>(senderList); senderList.Add(this.local_uid); WriteDebug(this.local_uid + " following proxy path " + key + " to " + GetStringOf(hintPath)); SendMemoryToPeer sendMsg = new SendMemoryToPeer(PROXY_PREFIX + key, hintPath); ResponseFollowProxy(sendMsg.MemBuffer, PROXY_PREFIX + key, senderList); return sendMsg; } } return null; }
/// <summary> /// TODO: add in some code to reduce round-trips to simple data types /// </summary> /// <param name="reader">File to read</param> /// <param name="sentFromList">Sent from list</param> /// <param name="getEntriesFromSender">This function fills in a list of entries that need to be requested from the sender</param> /// <param name="addEntriesToSender">These are entries that the sender does not know about</param> /// <seealso cref="ReadHeadStub"/> private void ReadDictionaryTextFile(StreamReader reader, List<int> sentFromList, List<DataHeader> getEntriesFromSender, List<SendMemoryToPeer> addEntriesToSender) { // 0 - key name // 1 - owner // 2 - revision // 3 - rw flag // 4 - MIME type // WriteDebug(this.local_uid + " ReadDictionaryTextFile"); string nsLine = reader.ReadLine(); string[] nsLineParts = nsLine.Split('\t'); System.Diagnostics.Debug.Assert(nsLineParts[0] == DATA_NAMESPACE + "/"); // if the owner of the dictionary is the same as myself, skip reading the changes if (nsLineParts[1] == this.local_uid.ToString()) { throw new NotSupportedException("ReadDictionaryTextFile cannot read itself");// i want to see if this actually can happen (only when multiple connections happen on the same server) } int itemCount = int.Parse(nsLineParts[4]); // count of all the items in the dictionary List<DataEntry> entriesCovered = new List<DataEntry>(itemCount + this.data.Count); for (int i = 0; i < itemCount; i++) { nsLine = reader.ReadLine(); nsLineParts = nsLine.Split('\t'); //WriteDebug(nsLine); DataHeader getEntry = null; SendMemoryToPeer addEntryToSender = null; ETag tag = new ETag(int.Parse(nsLineParts[1]), int.Parse(nsLineParts[2])); // this entry is used only to call ReadMimeSimpleData DataEntry fakeEntry = new DataEntry("/fake", tag, new List<int>(0)); fakeEntry.ReadMimeSimpleData(nsLineParts[4]); dataLock.EnterReadLock(); try { if (this.data.ContainsKey(nsLineParts[0])) { entriesCovered.Add(this.data[nsLineParts[0]]); } } finally { dataLock.ExitReadLock(); } // the dictionary does not report the current sender so let's tack it on List<int> listOfSenders = new List<int>(GetArrayOf(nsLineParts[5])); if (!listOfSenders.Contains(this.remote_uid)) listOfSenders.Add(this.remote_uid); ResponseAction action = ReadDataStub(nsLineParts[0], fakeEntry.GetMime(), nsLineParts[1] + "." + nsLineParts[2], new List<int>( listOfSenders), out getEntry, out addEntryToSender); if (getEntry != null) { getEntriesFromSender.Add(getEntry); } if (addEntryToSender != null) { addEntriesToSender.Add(addEntryToSender); } if (action == ResponseAction.ForwardToAll) { listOfSenders.Clear(); } if (action != ResponseAction.DoNotForward) { DataEntry get = P2PDictionary.GetEntry( this.data, this.dataLock, nsLineParts[0]); System.Diagnostics.Debug.Assert(get != null); listOfSenders.Add(this.local_uid); SendBroadcastMemory msg = new SendBroadcastMemory(get.key , listOfSenders); WriteMethodPush(get.key, listOfSenders, null, 0, get.GetMime(), get.GetETag(), get.IsEmpty, false, msg.MemBuffer); this.controller.BroadcastToWire(msg); } } // now check to see which dictionary entries that the sender does not have; i'll send my entries to the caller this.dataLock.EnterWriteLock(); try { foreach (KeyValuePair<string, DataEntry> senderDoesNotHave in this.data.SkipWhile(x => entriesCovered.Contains(x.Value))) { DataEntry get = senderDoesNotHave.Value; // i know about something that the sender does not know SendMemoryToPeer mem = new SendMemoryToPeer(get.key, sentFromList); WriteMethodPush(get.key, GetListOfThisLocalID(), null, 0, get.GetMime(), get.GetETag(), get.IsEmpty, false, mem.MemBuffer); addEntriesToSender.Add(mem); } } finally { this.dataLock.ExitWriteLock();} }
/// <summary> /// Reads data using only header information. Can be used by ReadDictionary /// so it handles deleted content too. /// </summary> /// <param name="contentLocation">Location of the data item without /proxy</param> /// <param name="contentType">GetMime()</param> /// <param name="eTag">Version number</param> /// <param name="addEntryToSender">These are entries that the sender does not know about</param> /// <param name="getEntryFromSender">This function fills in a list of entries that need to be requested from the sender</param> private ResponseAction ReadDataStub(string contentLocation, string contentType, string eTag, List<int> sentFromList, out DataHeader getEntryFromSender, out SendMemoryToPeer addEntryToSender) { ResponseAction success = ResponseAction.DoNotForward; ETag tag = ReadETag(eTag); DataEntry get = null; getEntryFromSender = null; addEntryToSender = null; // create a stub of the item DataEntry create = new DataEntry(contentLocation, tag, sentFromList); create.subscribed = keysToListen.IsSubscribed(contentLocation); create.SetMime(contentType); // manually erase the value (TODO, don't erase the value) // always default to singleton because we assume that a GET is required to complete the request if (create.type != Data.ValueType.Removed) create.value = DataMissing.Singleton; this.dataLock.EnterUpgradeableReadLock(); try { if (this.data.ContainsKey(contentLocation)) { // update the version number of the stub get = this.data[contentLocation]; } else { this.dataLock.EnterWriteLock(); try { this.data.Add(contentLocation, create); } finally { this.dataLock.ExitWriteLock(); } if (create.subscribed && DataMissing.IsSingleton(create.value)) { // we'll have to wait for the data to arrive on the wire // actually get the data getEntryFromSender = new DataHeader(contentLocation, tag, sentFromList); success = ResponseAction.DoNotForward; } else { success = ResponseAction.ForwardToSuccessor; } } } finally { this.dataLock.ExitUpgradeableReadLock(); } if (get != null) { lock (get) { if (create.subscribed) { ETagCompare compareResult = ETag.CompareETags(tag, get.GetETag()); if (compareResult == ETagCompare.FirstIsNewer || compareResult == ETagCompare.Same || compareResult == ETagCompare.Conflict) { getEntryFromSender = new DataHeader(create.key, create.GetETag(), sentFromList); success = ResponseAction.DoNotForward; } else //if (compareResult == ETagCompare.SecondIsNewer ) { // i know about something newer than the sender, tell the sender //SendMemoryToPeer mem = new SendMemoryToPeer(get.key,sentFromList); //ResponseHeadStub(HEAD, get.key, GetListOfThisLocalID(), 0, get.GetMime(), get.GetETag(), get.IsEmpty, mem.MemBuffer, false); //addEntryToSender = mem; // well, predecessor already been handled above, so we only need to tell // the others success = ResponseAction.ForwardToAll; } } else { // not subscribed // just record the fact that there is newer data on the wire; cannot resolve conflicts without being a subscriber ETagCompare compareResult = ETag.CompareETags(create.GetETag(), get.GetETag()); if (compareResult == ETagCompare.FirstIsNewer || compareResult == ETagCompare.Same || compareResult == ETagCompare.Conflict) { get.lastOwnerID = create.lastOwnerID; get.lastOwnerRevision = create.lastOwnerRevision; get.value = DataMissing.Singleton; if (compareResult != ETagCompare.Same) { get.senderPath = create.senderPath; success = ResponseAction.ForwardToSuccessor; } else success = ResponseAction.DoNotForward; } else // if (compareResult == ETagCompare.SecondIsNewer ) { //// i know about something newer than the sender //SendMemoryToPeer mem = new SendMemoryToPeer(get.key,sentFromList); //ResponseHeadStub(HEAD, get.key, GetListOfThisLocalID(), 0, get.GetMime(), get.GetETag(), get.IsEmpty, mem.MemBuffer, false); //addEntryToSender = mem; // tell the others too (already told predecessor above) success = ResponseAction.ForwardToAll; } } } } return success; }
int IMessageController.SendToPeer(SendMemoryToPeer message) { if (this.IsAlive) return CastTargetController.SendToPeer(message); else return 0; }