private static MessageProxy CreateRequestProxy(MessageAssembler operation) { Type requestType; int[] requestTypeInput; if (operation.OperationType == MethodType.Unary || operation.OperationType == MethodType.ServerStreaming) { requestType = operation.RequestType; requestTypeInput = operation.RequestTypeInput; } else { requestType = operation.HeaderRequestType ?? typeof(Message); requestTypeInput = operation.HeaderRequestTypeInput; } if (requestTypeInput.Length == 0) { return(new MessageProxy(Array.Empty <string>(), requestType)); } var requestNames = new string[requestTypeInput.Length]; for (var i = 0; i < requestNames.Length; i++) { var index = requestTypeInput[i]; requestNames[i] = operation.Parameters[index].Name; } return(new MessageProxy(requestNames, requestType)); }
private void BuildWriteServerStreamingResult(ILGenerator body, MessageAssembler message) { if (message.HeaderResponseType == null && !message.IsAsync) { // return ServerChannelAdapter.ServerStreaming(service.Simple()); var channelAdapter = typeof(ServerChannelAdapter) .StaticMethod(nameof(ServerChannelAdapter.ServerStreaming)) .MakeGenericMethod(message.ResponseType.GenericTypeArguments[0]); body.Emit(OpCodes.Call, channelAdapter); } else if (message.HeaderResponseType == null && message.IsAsync) { // return ServerChannelAdapter.ServerStreamingTask(service.SimpleTask()); var channelAdapter = typeof(ServerChannelAdapter) .StaticMethod(message.Operation.ReturnType.IsValueTask() ? nameof(ServerChannelAdapter.ServerStreamingValueTask) : nameof(ServerChannelAdapter.ServerStreamingTask)) .MakeGenericMethod(message.ResponseType.GenericTypeArguments[0]); body.Emit(OpCodes.Call, channelAdapter); } else { // return ServerChannelAdapter.ServerStreamingHeaderTask(service.HeaderTask(), AdaptHeaderTask); var adapterField = BuildServerStreamingResultAdapter(message); body.Emit(OpCodes.Ldarg_0); body.Emit(OpCodes.Ldfld, adapterField); var channelAdapter = typeof(ServerChannelAdapter) .StaticMethod(message.Operation.ReturnType.IsValueTask() ? nameof(ServerChannelAdapter.ServerStreamingHeaderValueTask) : nameof(ServerChannelAdapter.ServerStreamingHeaderTask)) .MakeGenericMethod(message.Operation.ReturnType.GetGenericArguments()[0], message.HeaderResponseType, message.ResponseType.GenericTypeArguments[0]); body.Emit(OpCodes.Call, channelAdapter); } }
private FieldBuilder BuildServerStreamingResultAdapter(MessageAssembler message) { // private static (Message<string, int>, IAsyncEnumerable<int>) AdaptHeaderTask((string, IAsyncEnumerable<int>, int) result) var parameterType = message.Operation.ReturnType.GetGenericArguments()[0]; var returnType = typeof(ValueTuple <,>).MakeGenericType( message.HeaderResponseType, typeof(IAsyncEnumerable <>).MakeGenericType(message.ResponseType.GenericTypeArguments[0])); var method = _typeBuilder .DefineMethod( GetUniqueMemberName("__Adapt" + message.Operation.Name + "Response"), MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, returnType, new[] { parameterType }); var body = method.GetILGenerator(); // return (new Message<string, int>(result.Item1, result.Item3), result.Item2); var headerPropertiesCount = message.HeaderResponseTypeInput.Length; for (var i = 0; i < headerPropertiesCount; i++) { body.Emit(OpCodes.Ldarg_0); var index = message.HeaderResponseTypeInput[i] + 1; var fieldName = "Item" + index.ToString(CultureInfo.InvariantCulture); body.Emit(OpCodes.Ldfld, parameterType.InstanceFiled(fieldName)); } // new Message<string, int>() body.Emit(OpCodes.Newobj, message.HeaderResponseType !.Constructor(headerPropertiesCount)); // push stream var streamFieldName = "Item" + (message.ResponseTypeIndex + 1).ToString(CultureInfo.InvariantCulture); body.Emit(OpCodes.Ldarg_0); body.Emit(OpCodes.Ldfld, parameterType.InstanceFiled(streamFieldName)); body.Emit(OpCodes.Newobj, returnType !.Constructor(2)); body.Emit(OpCodes.Ret); var delegateType = typeof(Func <,>).MakeGenericType(parameterType, returnType); // private readonly Func<(string, IAsyncEnumerable<int>, int), (Message<string, int>, IAsyncEnumerable<int>)> _adaptHeaderTask = AdaptHeaderTask; var field = _typeBuilder .DefineField( GetUniqueMemberName("__" + message.Operation.Name + "ResponseAdapter"), delegateType, FieldAttributes.Private | FieldAttributes.InitOnly); // _adaptHeaderTask = AdaptHeaderTask _ctor.Emit(OpCodes.Ldarg_0); _ctor.Emit(OpCodes.Ldnull); _ctor.Emit(OpCodes.Ldftn, method); _ctor.Emit(OpCodes.Newobj, delegateType.Constructor(typeof(object), typeof(IntPtr))); _ctor.Emit(OpCodes.Stfld, field); return(field); }
public void TestMultipleMessages() { byte[] data = SerializeAll("one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"); IList<Chunk> chunks = Chop(data, 100); MessageAssembler assembler = new MessageAssembler(Formatter); IList<object> result = new List<object>(); foreach(Chunk chunk in chunks) { IList<object> messages = assembler.Assemble(chunk.Bytes, chunk.Size); foreach (object message in messages) { result.Add(message); } } Assert.NotNull(result); Assert.AreEqual(10, result.Count); Assert.AreEqual("one", result[0]); Assert.AreEqual("two", result[1]); Assert.AreEqual("three", result[2]); Assert.AreEqual("four", result[3]); Assert.AreEqual("five", result[4]); Assert.AreEqual("six", result[5]); Assert.AreEqual("seven", result[6]); Assert.AreEqual("eight", result[7]); Assert.AreEqual("nine", result[8]); Assert.AreEqual("ten", result[9]); }
private void BuildUnary(ILGenerator body, MessageAssembler decorator, OperationDescription operation) { // optionsBuilder InitializeCallOptionsBuilder(body, decorator); // var call = new UnaryCall<TRequest, TResponse>(method, CallInvoker, optionsBuilder) var callType = typeof(UnaryCall <,>) .MakeGenericType(decorator.RequestType, decorator.ResponseType); InitializeCall(body, operation, callType); // call.Invoke(message) body.Emit(OpCodes.Ldloca_S, 1); // call PushMessage(body, decorator.RequestType, decorator.RequestTypeInput); // message var invokeMethod = callType.InstanceGenericMethod( decorator.IsAsync ? "InvokeAsync" : "Invoke", decorator.ResponseType.IsGenericType ? 1 : 0); if (decorator.ResponseType.IsGenericType) { invokeMethod = invokeMethod.MakeGenericMethod(decorator.ResponseType.GenericTypeArguments); } body.Emit(OpCodes.Call, invokeMethod); // Task => new ValueTask if (decorator.Operation.ReturnType.IsValueTask()) { body.Emit(OpCodes.Newobj, decorator.Operation.ReturnType.Constructor(invokeMethod.ReturnType)); } body.Emit(OpCodes.Ret); }
private void BuildServerStreaming(ILGenerator body, MessageAssembler message, Type serviceType) { // service body.Emit(OpCodes.Ldarg_1); for (var i = 0; i < message.Parameters.Length; i++) { var parameter = message.Parameters[i]; if (message.ContextInput.Contains(i)) { PushContext(body, 3, parameter.ParameterType); } else { var propertyName = "Value" + (Array.IndexOf(message.RequestTypeInput, i) + 1); // request.Value1 body.Emit(OpCodes.Ldarg_2); body.Emit(OpCodes.Callvirt, message.RequestType.InstanceProperty(propertyName).GetMethod); } } // service.Method CallContractMethod(body, message, serviceType); BuildWriteServerStreamingResult(body, message); body.Emit(OpCodes.Ret); }
private void InitializeCallOptionsBuilder(ILGenerator body, MessageAssembler operation) { // var optionsBuilder = new CallOptionsBuilder(DefaultOptions) body.DeclareLocal(typeof(CallOptionsBuilder)); body.Emit(OpCodes.Ldarg_0); // this body.Emit(OpCodes.Ldfld, _callOptionsFactoryField); // DefaultOptions body.Emit(OpCodes.Newobj, typeof(CallOptionsBuilder).Constructor(typeof(Func <CallOptions>))); body.Emit(OpCodes.Stloc_0); // optionsBuilder = optionsBuilder.With() foreach (var i in operation.ContextInput) { body.Emit(OpCodes.Ldloca_S, 0); // optionsBuilder body.EmitLdarg(i + 1); // parameter var parameterType = operation.Parameters[i].ParameterType; Type?nullable = null; if (parameterType.IsValueType) { nullable = Nullable.GetUnderlyingType(parameterType); if (nullable == null) { // CancellationToken => CancellationToken? body.Emit(OpCodes.Newobj, typeof(Nullable <>).MakeGenericType(parameterType).Constructor(parameterType)); } } var withMethodName = "With" + (nullable?.Name ?? parameterType.Name); body.Emit(OpCodes.Call, typeof(CallOptionsBuilder).InstanceMethod(withMethodName)); // .With body.Emit(OpCodes.Stloc_0); } }
private void PushHeaderProperty(ILGenerator body, MessageAssembler message, int parameterIndex) { var propertyName = "Value" + (Array.IndexOf(message.HeaderRequestTypeInput, parameterIndex) + 1); body.Emit(OpCodes.Ldarg_2); // requestHeader body.Emit(OpCodes.Callvirt, message.HeaderRequestType !.InstanceProperty(propertyName).GetMethod); // requestHeader }
private void ImplementMethod(MessageAssembler decorator, OperationDescription operation) { var body = CreateMethodWithSignature(decorator.Operation); switch (decorator.OperationType) { case MethodType.Unary: BuildUnary(body, decorator, operation); break; case MethodType.ClientStreaming: BuildClientStreaming(body, operation); break; case MethodType.ServerStreaming: BuildServerStreaming(body, operation); break; case MethodType.DuplexStreaming: BuildDuplexStreaming(body, operation); break; default: throw new NotImplementedException("{0} operation is not implemented.".FormatWith(decorator.OperationType)); } }
private void BuildDuplexStreaming(ILGenerator body, MessageAssembler message, Type serviceType, FieldBuilder?inputHeadersMarshallerFiled, FieldBuilder?outputHeadersMarshaller) { DeclareHeaderValues(body, message, inputHeadersMarshallerFiled, 4); body.Emit(OpCodes.Ldarg_1); // service for (var i = 0; i < message.Parameters.Length; i++) { var parameter = message.Parameters[i]; if (message.ContextInput.Contains(i)) { PushContext(body, 4, parameter.ParameterType); } else if (message.HeaderRequestTypeInput.Contains(i)) { PushHeaderProperty(body, message, i); } else { // ReadClientStream() body.Emit(OpCodes.Ldarg_2); // input body.EmitLdarg(4); // context body.Emit(OpCodes.Call, typeof(ServerChannelAdapter).StaticMethod(nameof(ServerChannelAdapter.ReadClientStream)).MakeGenericMethod(message.RequestType.GenericTypeArguments)); } } // service.Method CallContractMethod(body, message, serviceType); BuildWriteServerStreamingResult(body, message, outputHeadersMarshaller); body.Emit(OpCodes.Ret); }
internal static IEnumerable <ParameterInfo> GetRequestParameters(MessageAssembler message) { for (var i = 0; i < message.RequestTypeInput.Length; i++) { yield return(message.Parameters[message.RequestTypeInput[i]]); } }
private void BuildDuplexStreaming(ILGenerator body, MessageAssembler message, Type serviceType) { body.Emit(OpCodes.Ldarg_1); // service for (var i = 0; i < message.Parameters.Length; i++) { var parameter = message.Parameters[i]; if (message.ContextInput.Contains(i)) { PushContext(body, 4, parameter.ParameterType); } else if (message.HeaderRequestTypeInput.Contains(i)) { PushHeaderProperty(body, message, i); } else { body.Emit(OpCodes.Ldarg_3); // request } } // service.Method CallContractMethod(body, message, serviceType); BuildWriteServerStreamingResult(body, message); body.Emit(OpCodes.Ret); }
public void GetMessageTestOK() { //parameters List <List <string> > allMessagesOk = new List <List <string> >(); List <List <string> > allMessagesBad = new List <List <string> >(); allMessagesOk.Add(new List <string>(new string[] { "este", "", "", "mensaje", "" })); allMessagesOk.Add(new List <string>(new string[] { "", "es", "", "", "secreto" })); allMessagesOk.Add(new List <string>(new string[] { "este", "", "un", "", "" })); allMessagesBad.Add(new List <string>(new string[] { "este", "", "", "mensaje", "" })); allMessagesBad.Add(new List <string>(new string[] { "", "es", "", "", "secreto" })); allMessagesBad.Add(new List <string>(new string[] { "este", "", "un", "" })); string messageExpected = "este es un mensaje secreto"; MessageAssembler messageAssembler = new MessageAssembler(); // Act string messageOk = messageAssembler.GetMessage(allMessagesOk); // Assert Assert.AreEqual(messageExpected, messageOk); Assert.Pass(); // Act string messageBad = messageAssembler.GetMessage(allMessagesBad); // Assert Assert.AreEqual(messageExpected, messageOk); Assert.Pass(); }
private static StreamProxy?CreateResponseStreamProxy(MessageAssembler operation) { if (operation.OperationType != MethodType.ServerStreaming && operation.OperationType != MethodType.DuplexStreaming) { return(null); } return(new StreamProxy(operation.ResponseType.GenericTypeArguments[0])); }
public ProxyFactory(MethodInfo contractMethodDefinition) { var operation = new MessageAssembler(contractMethodDefinition); RequestProxy = CreateRequestProxy(operation); ResponseProxy = CreateResponseProxy(operation); RequestStreamProxy = CreateRequestStreamProxy(operation); ResponseStreamProxy = CreateResponseStreamProxy(operation); }
public static bool renameFile(string sourceFilePath, string destFilePath, string lastWriteTime) { connectServer(); byte[] message = MessageAssembler.renameFile(sourceFilePath, destFilePath, lastWriteTime); VerifyHandler.postMessage(clientSocket, message); if (!VerifyHandler.verify(clientSocket)) { return(false); } return(true); }
private ILGenerator CreateMethodWithSignature(MessageAssembler message, Type serviceType, string methodName) { switch (message.OperationType) { case MethodType.Unary: // Task<TResponse> Invoke(TService service, TRequest request, ServerCallContext context) return(_typeBuilder .DefineMethod( methodName, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final, typeof(Task <>).MakeGenericType(message.ResponseType), new[] { serviceType, message.RequestType, typeof(ServerCallContext) }) .GetILGenerator()); case MethodType.ClientStreaming: // Task<TResponse> Invoke(TService service, IAsyncStreamReader<TRequest> request, ServerCallContext context) return(_typeBuilder .DefineMethod( methodName, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final, typeof(Task <>).MakeGenericType(message.ResponseType), new[] { serviceType, typeof(IAsyncStreamReader <>).MakeGenericType(message.RequestType), typeof(ServerCallContext) }) .GetILGenerator()); case MethodType.ServerStreaming: // Task Invoke(TService service, TRequest request, IServerStreamWriter<TResponse> stream, ServerCallContext context) return(_typeBuilder .DefineMethod( methodName, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final, typeof(Task), new[] { serviceType, message.RequestType, typeof(IServerStreamWriter <>).MakeGenericType(message.ResponseType), typeof(ServerCallContext) }) .GetILGenerator()); case MethodType.DuplexStreaming: // Task Invoke(TService service, IAsyncStreamReader<TRequest> request, IServerStreamWriter<TResponse> response, ServerCallContext context) return(_typeBuilder .DefineMethod( methodName, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final, typeof(Task), new[] { serviceType, typeof(IAsyncStreamReader <>).MakeGenericType(message.RequestType), typeof(IServerStreamWriter <>).MakeGenericType(message.ResponseType), typeof(ServerCallContext) }) .GetILGenerator()); } throw new NotImplementedException("{0} operation is not implemented.".FormatWith(message.OperationType)); }
public static bool createFile(string path, string lastWriteTime) { connectServer(); byte[] message = MessageAssembler.createFile(path, lastWriteTime); VerifyHandler.postMessage(clientSocket, message); if (!VerifyHandler.verify(clientSocket)) { return(false); } return(true); }
public static bool regist(string account, string password) { connectServer(); byte[] message = MessageAssembler.registMsgAssemble(account, password); VerifyHandler.postMessage(clientSocket, message); if (!VerifyHandler.verify(clientSocket)) { return(false); } return(true); }
public static bool delete(string path) { connectServer(); byte[] message = MessageAssembler.delete(path); VerifyHandler.postMessage(clientSocket, message); if (!VerifyHandler.verify(clientSocket)) { return(false); } return(true); }
public async Task <IAuthenticationResult> Login(string apiKey, IUserCredentials userCredentials) { var request = MessageAssembler.ToMessage <IUserCredentials, UserCredentials>(userCredentials); var response = await WithAnonymousClient() .ForApplication(apiKey) .OnVersion(2) .Post(request) .To("session") .ForHttpResponseMessageReader(); return(await BuildAuthenticationResult(response)); }
private static bool IsSupportedContextInput(MessageAssembler message) { for (var i = 0; i < message.ContextInput.Length; i++) { var input = message.ContextInput[i]; if (!ServerChannelAdapter.TryGetServiceContextOptionMethod(message.Parameters[input].ParameterType)) { return(false); } } return(true); }
internal static (Type?Type, ParameterInfo Parameter) GetResponseType(MessageAssembler message) { // do not return typeof(void) => Swashbuckle schema generation error var arguments = message.ResponseType.GetGenericArguments(); var responseType = arguments.Length == 0 ? null : arguments[0]; if (message.OperationType == MethodType.ServerStreaming || message.OperationType == MethodType.DuplexStreaming) { responseType = typeof(IAsyncEnumerable <>).MakeGenericType(responseType !); } return(responseType, message.Operation.ReturnParameter); }
private void CallContractMethod(ILGenerator body, MessageAssembler message, Type serviceType) { var parameters = new Type[message.Parameters.Length]; for (var i = 0; i < parameters.Length; i++) { parameters[i] = message.Parameters[i].ParameterType; } var method = serviceType.InstanceMethod(message.Operation.Name, parameters); body.Emit(OpCodes.Callvirt, method); }
private void DeclareHeaderValues(ILGenerator body, MessageAssembler message, FieldBuilder?headersMarshallerFiled, int contextParameterIndex) { if (headersMarshallerFiled != null) { body.DeclareLocal(message.HeaderRequestType !); // var headers body.Emit(OpCodes.Ldarg_0); body.Emit(OpCodes.Ldfld, headersMarshallerFiled); // static Marshaller<> body.EmitLdarg(contextParameterIndex); // context body.Emit(OpCodes.Call, typeof(ServerChannelAdapter).StaticMethod(nameof(ServerChannelAdapter.GetMethodInputHeader)).MakeGenericMethod(message.HeaderRequestType)); body.Emit(OpCodes.Stloc_0); } }
public static void syncNow() { connectServer(); string paraHome = CommonStaticVariables.homePath.Substring(0, CommonStaticVariables.homePath.Length - 1); byte[] message = MessageAssembler.syncNow(); VerifyHandler.postMessage(clientSocket, message); VerifyHandler.verify(clientSocket); findFileAndSync(paraHome, paraHome); // flag = 2 表示已同步完成 VerifyHandler.postMessage(clientSocket, MessageAssembler.assembleDone()); }
public static bool modifyFile(string sourceFilePath, string fullPath, string lastWriteTime) { connectServer(); byte[] message = MessageAssembler.modifyFile(sourceFilePath, lastWriteTime); VerifyHandler.postMessage(clientSocket, message); VerifyHandler.verify(clientSocket); FileTransferHandler.postFile(clientSocket, fullPath); if (!VerifyHandler.verify(clientSocket)) { return(false); } return(true); }
private void BuildUnary(ILGenerator body, MessageAssembler message, Type serviceType) { // service body.Emit(OpCodes.Ldarg_1); for (var i = 0; i < message.Parameters.Length; i++) { var parameter = message.Parameters[i]; if (message.ContextInput.Contains(i)) { PushContext(body, 3, parameter.ParameterType); } else { var propertyName = "Value" + (Array.IndexOf(message.RequestTypeInput, i) + 1); // request.Value1 body.Emit(OpCodes.Ldarg_2); body.Emit(OpCodes.Callvirt, message.RequestType.InstanceProperty(propertyName).GetMethod); } } // service.Method CallContractMethod(body, message, serviceType); if (message.IsAsync) { AdaptSyncUnaryCallResult(body, message); } else { if (message.ResponseType.IsGenericType) { // new Message<T> body.Emit(OpCodes.Newobj, message.ResponseType.Constructor(message.ResponseType.GenericTypeArguments)); } else { // new Message body.Emit(OpCodes.Newobj, message.ResponseType.Constructor()); } // Task.FromResult body.Emit(OpCodes.Call, typeof(Task).StaticMethod(nameof(Task.FromResult)).MakeGenericMethod(message.ResponseType)); } body.Emit(OpCodes.Ret); }
public void GetResponseType(MethodInfo method) { var expected = method.GetCustomAttribute <ResponseMetadataAttribute>(); var message = new MessageAssembler(method); var responseType = ApiDescriptionGenerator.GetResponseType(message); var responseHeaders = ApiDescriptionGenerator.GetResponseHeaderParameters(message); responseType.Type.ShouldBe(expected !.Type); responseType.Parameter.ShouldBe(method.ReturnParameter); responseHeaders.Length.ShouldBe(expected.HeaderTypes.Length); for (var i = 0; i < expected.HeaderTypes.Length; i++) { responseHeaders[i].Type.ShouldBe(expected.HeaderTypes[i]); responseHeaders[i].Name.ShouldBe(expected.HeaderNames[i]); } }
private static MessageProxy CreateResponseProxy(MessageAssembler operation) { Type responseType; string[] names; if (operation.OperationType == MethodType.Unary || operation.OperationType == MethodType.ClientStreaming) { responseType = operation.ResponseType; names = operation.ResponseType == typeof(Message) ? Array.Empty <string>() : MessageProxy.UnaryResultNames; } else { responseType = operation.HeaderResponseType ?? typeof(Message); names = operation.GetResponseHeaderNames(); } return(new MessageProxy(names, responseType)); }
public static bool logout(string account, string password) { if (!clientSocket.Connected) { return(true); } byte[] message = MessageAssembler.logoutMsgAssemble(account, password); VerifyHandler.postMessage(clientSocket, message); if (!VerifyHandler.verify(clientSocket)) { return(false); } clientSocket.Shutdown(SocketShutdown.Both); clientSocket.Close(); return(true); }
/// <summary> /// Attempts to assemble the chunk into a completed message. /// If this is the last chunk of a message, the completed message /// is returned, and any unsatisfied dependencies that are now /// satisfied by the completed message are recursively assembled. /// All such assembled message are also returned via the enumeration. /// </summary> /// <remarks> /// This method is called by <see cref="Add"/> and <see cref="FlushWaitingChunks"/>, /// after all chunk dependencies are satisfied. /// </remarks> /// <param name="chunk">The chunk to assemble.</param> /// <returns> /// The enumeration of decoded messages. /// The enumeration may be empty or may /// have arbitrarily many members if dependencies were /// (recursively) satisfied by this chunk. /// </returns> protected IEnumerable<object> Assemble(Chunk chunk) { // Process single-chunk messages immediately. if (chunk.NumberOfChunksInMessage <= 1) { // Don't create a MessageAssembler for singleton chunks. // Instead, just return the message immediately. using (MemoryStream ms = new MemoryStream(chunk.Data)) { yield return this.m_Formatter.Deserialize(ms); // The message has been completed, so flush any chunks which were waiting for it. foreach (object dependent in this.FlushWaitingChunks(chunk.MessageSequence)) yield return dependent; yield break; } } // For multi-chunk messages, we first attempt to find an existing MessageAssembler // instance for the message to which the chunk belongs (based on the range of chunk // sequence numbers the message spans). MessageAssembler assembler = this.NewestMessageAssember, previous = null; object message; for (; ; ) { bool done, remove, complete; // If there does not exist any assembler for which IsInRange(chunk) returned true, // create one to hold the chunk. if (assembler == null) { Debug.WriteLine(string.Format("Creating a new MessageAssembler to manage multipart message (message #{0}, chunks #{1}-{2})", chunk.MessageSequence, chunk.ChunkSequenceInMessage + 1, chunk.NumberOfChunksInMessage), this.GetType().ToString()); assembler = new MessageAssembler(chunk.MessageSequence, chunk.NumberOfChunksInMessage, this.m_Formatter); // Insert the assembler as the first entry in our linked list, // since it is most likely to be used by subsequent chunks. assembler.NextOldestAssembler = this.NewestMessageAssember; this.NewestMessageAssember = assembler; } // See if the chunk belongs to the current assembler. if (assembler.MessageSequence == chunk.MessageSequence) { // If so, add the chunk to it, and we can stop searching. assembler.Add(chunk); done = true; // If the message has been fully assembled, process it // and remove the no-longer-needed assembler. complete = assembler.IsComplete; if (complete) { message = assembler.DeserializeMessage(); remove = true; } else { message = null; remove = false; } } else if (assembler.MessageSequence < chunk.OldestRecoverableMessage) { // For each message assembler that is waiting for more chunks (and to which the current // chunk does not belong), make sure it will be possible to complete the message in // the future. If the sender reports that its OldestRecoverableFrame is greater than // the sequence number of any frame yet needed to complete the message, then no // NACK we send can ever satisfy our needs, so we discard the message completely // (removing the assembler from the linked list). Debug.WriteLine(string.Format("### Giving up on message #{0} (chunks #{0}-{1}): the oldest available chunk is {2}!", chunk.MessageSequence, chunk.ChunkSequenceInMessage + 1, chunk.NumberOfChunksInMessage, chunk.OldestRecoverableMessage), this.GetType().ToString()); remove = true; message = null; done = false; complete = false; } else { remove = false; message = null; done = false; complete = false; } // If the assembler is no longer useful, remove it from the linked list. // (There are a couple of conditions, above, under which this might happen.) if (remove) { if (previous == null) { this.NewestMessageAssember = assembler.NextOldestAssembler; } else { previous.NextOldestAssembler = assembler.NextOldestAssembler; } } // If an assembler was found which accepted the chunk, we're done. // (There are a couple of conditions, above, under which this might happen.) if (done) { if (complete) { yield return message; // The message has been completed, so flush any chunks which were waiting for it. foreach (object dependent in this.FlushWaitingChunks(chunk.MessageSequence)) yield return dependent; } yield break; } else { // Get the next assembler. Do not break from the loop if there // is no "next" assembler, since one will be created. previous = assembler; assembler = assembler.NextOldestAssembler; } } }