///// <summary> ///// respond only by header ///// </summary> ///// <param name="verb">GET or HEAD</param> ///// <param name="resource">Same as key or contentLocation</param> ///// <param name="senderList"></param> ///// <param name="contentLength"></param> ///// <param name="mimeType"></param> ///// <param name="lastVer"></param> ///// <param name="isDeleted">Indicates that the current entry is deleted</param> ///// <param name="willClose">Writes a close message in the header</param> ///// <param name="bufferedOutput">A memory buffer that will be filled with contents produced ///// from this method</param> //private void ResponseHeadStub(string verb, string resource, List<int> senderList, List<int> proxyResponsePath, int contentLength, // string mimeType, ETag lastVer, bool isDeleted, bool willClose, MemoryStream bufferedOutput) //{ // StreamWriter writer = new StreamWriter(bufferedOutput, Encoding.ASCII); // writer.NewLine = NEWLINE; // if (isDeleted) // { // WriteResponseDeleted(bufferedOutput, resource, senderList, proxyResponsePath, lastVer.UID, lastVer.Revision); // } // else // { // WriteResponseHeader(writer, resource, mimeType, contentLength, lastVer.UID, lastVer.Revision, senderList, proxyResponsePath, verb, willClose); // } //} // respond to a GET request, HEAD request, and push data on wire private void Response(string verb, string resource, List<int> senderList,List<int> proxyResponsePath, DataEntry entry, MemoryStream bufferedOutput, bool willClose) { lock (entry) { if (entry.IsEmpty) { WriteResponseDeleted(bufferedOutput, resource, senderList, proxyResponsePath, entry.lastOwnerID, entry.lastOwnerRevision); } else if (entry.IsSimpleValue || entry.type == DataEntry.ValueType.String) { StreamWriter writer = new StreamWriter(bufferedOutput, Encoding.ASCII); writer.NewLine = NEWLINE; string translation =""; if (entry.value != null) translation = entry.value.ToString(); byte[] bytesToWrite = System.Text.Encoding.UTF8.GetBytes(translation); WriteResponseHeader(writer, resource, entry.GetMime(), bytesToWrite.Length, entry.lastOwnerID, entry.lastOwnerRevision, senderList, proxyResponsePath, verb, willClose); writer.Flush(); if (verb == GET) { bufferedOutput.Write(bytesToWrite, 0, bytesToWrite.Length); //StreamWriter tw = new StreamWriter(bufferedOutput, Encoding.UTF8); // tw.NewLine = NEWLINE; // tw.BaseStream.Seek(0, SeekOrigin.End); // tw.Write(translation); // tw.Flush(); } writer.Flush(); } else if (entry.IsComplexValue) { if (entry.type == DataEntry.ValueType.Binary) { StreamWriter writer = new StreamWriter(bufferedOutput, Encoding.ASCII); writer.NewLine = NEWLINE; byte[] bentry = (byte[])entry.value; WriteResponseHeader(writer, resource, entry.GetMime(), bentry.Length, entry.lastOwnerID, entry.lastOwnerRevision, senderList, proxyResponsePath, verb, willClose); writer.Flush(); if (verb == GET) { bufferedOutput.Write(bentry, 0, bentry.Length); } } else if (entry.type == DataEntry.ValueType.Object) { StreamWriter writer = new StreamWriter(bufferedOutput, Encoding.ASCII); writer.NewLine = NEWLINE; MemoryStream bentry; try { bentry = new MemoryStream(); BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(bentry, entry.value); } catch { throw; } System.Diagnostics.Debug.Assert(bentry.Length <= int.MaxValue); WriteResponseHeader(writer, resource, entry.GetMime(), (int)bentry.Length, entry.lastOwnerID, entry.lastOwnerRevision, senderList, proxyResponsePath, verb, willClose); writer.Flush(); if (verb == GET) { bentry.WriteTo(bufferedOutput); } } else if (entry.type == Data.ValueType.Unknown) { StreamWriter writer = new StreamWriter(bufferedOutput, Encoding.ASCII); writer.NewLine = NEWLINE; byte[] bentry = ((DataUnsupported) entry.value).GetPayload(); WriteResponseHeader(writer, resource, entry.GetMime(), bentry.Length, entry.lastOwnerID, entry.lastOwnerRevision, senderList, proxyResponsePath, verb, willClose); writer.Flush(); if (verb == GET) { bufferedOutput.Write(bentry, 0, bentry.Length); } } else { throw new NotImplementedException(); } } else { throw new NotImplementedException(); } } }
/// <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();} }