public UpStreamItemController <TResult> GetUpStreamController <TResult>(string target, int paramCount, bool downStream = false)
        {
            Future <TResult> future = new Future <TResult>();

            future.BeginProcess();

            long invocationId = System.Threading.Interlocked.Increment(ref this.lastInvocationId);

            string[] streamIds = new string[paramCount];
            for (int i = 0; i < paramCount; i++)
            {
                streamIds[i] = System.Threading.Interlocked.Increment(ref this.lastStreamId).ToString();
            }

            var controller = new UpStreamItemController <TResult>(this, invocationId, streamIds, future);

            Action <Message> callback = (Message msg) => {
                switch (msg.type)
                {
                // StreamItem message contains only one item.
                case MessageTypes.StreamItem:
                {
                    if (controller.IsCanceled)
                    {
                        break;
                    }

                    TResult item = (TResult)this.Protocol.ConvertTo(typeof(TResult), msg.item);

                    future.AssignItem(item);
                    break;
                }

                case MessageTypes.Completion:
                {
                    bool isSuccess = string.IsNullOrEmpty(msg.error);
                    if (isSuccess)
                    {
                        // While completion message must not contain any result, this should be future-proof
                        if (!controller.IsCanceled && msg.result != null)
                        {
                            TResult result = (TResult)this.Protocol.ConvertTo(typeof(TResult), msg.result);

                            future.AssignItem(result);
                        }

                        future.Finish();
                    }
                    else
                    {
                        var ex = new Exception(msg.error);
                        future.Fail(ex);
                    }
                    break;
                }
                }
            };

            var messageToSend = new Message
            {
                type         = downStream ? MessageTypes.StreamInvocation : MessageTypes.Invocation,
                invocationId = invocationId.ToString(),
                target       = target,
                arguments    = new object[0],
                streamIds    = streamIds,
                nonblocking  = false,
            };

            SendMessage(messageToSend);

            this.invocations.Add(invocationId, new InvocationDefinition {
                callback = callback, returnType = typeof(TResult)
            });

            return(controller);
        }
        public DownStreamItemController <TDown> GetDownStreamController <TDown>(string target, params object[] args)
        {
            long invocationId = System.Threading.Interlocked.Increment(ref this.lastInvocationId);

            var future = new Future <TDown>();

            future.BeginProcess();

            var controller = new DownStreamItemController <TDown>(this, invocationId, future);

            Action <Message> callback = (Message msg) =>
            {
                switch (msg.type)
                {
                // StreamItem message contains only one item.
                case MessageTypes.StreamItem:
                {
                    if (controller.IsCanceled)
                    {
                        break;
                    }

                    TDown item = (TDown)this.Protocol.ConvertTo(typeof(TDown), msg.item);

                    future.AssignItem(item);
                    break;
                }

                case MessageTypes.Completion:
                {
                    bool isSuccess = string.IsNullOrEmpty(msg.error);
                    if (isSuccess)
                    {
                        // While completion message must not contain any result, this should be future-proof
                        if (!controller.IsCanceled && msg.result != null)
                        {
                            TDown result = (TDown)this.Protocol.ConvertTo(typeof(TDown), msg.result);

                            future.AssignItem(result);
                        }

                        future.Finish();
                    }
                    else
                    {
                        future.Fail(new Exception(msg.error));
                    }
                    break;
                }
                }
            };

            var message = new Message
            {
                type         = MessageTypes.StreamInvocation,
                invocationId = invocationId.ToString(),
                target       = target,
                arguments    = args,
                nonblocking  = false,
            };

            SendMessage(message);

            if (callback != null)
            {
                this.invocations.Add(invocationId, new InvocationDefinition {
                    callback = callback, returnType = typeof(TDown)
                });
            }

            return(controller);
        }