public virtual Task <Message> ExecuteOperation(OperationContext context)
        {
            int requestId = Interlocked.Increment(ref _requestId);

            context.Message.AttachHeader(new OperationHeader(requestId, OperationType.Request));

            var continuation = new PendingOperation(requestId);

            if (!PendingOperationsByRequestId.TryAdd(requestId, continuation))
            {
                throw new Exception("This could happen only if requestId is duplicated");
            }

            var expiration = Task.Delay(TimeSpan.FromSeconds(ExpirationTimeout), continuation.Expiration.Token);

            expiration.ContinueWith((_) =>
            {
                PendingOperation dummy;
                if (PendingOperationsByRequestId.TryRemove(continuation.RequestId, out dummy))
                {
                    continuation.TCS.SetException(new Exception(string.Format("ExecuteOperation<{0}, {1}> has Expired after {2} sec", requestId, context.Message, ExpirationTimeout)));
                }
            });

            Log.Debug("Sending " + context.Message);
            Channel.Send(context.Message);
            MessageFactory.Free(context.Message);

            return(continuation.TCS.Task);
        }
        public Task <Message> SendRequestAsync(Message request)
        {
            _requestsMeter.Mark();

            //if operation header is already defined just use it eitherwise attach new one
            var  opHeader  = request.GetHeader <OperationHeader>();
            Guid requestId = Guid.NewGuid();

            if (opHeader == null)
            {
                request.AttachHeader(new OperationHeader(requestId, OperationType.Request, requestId));
            }
            else
            {
                requestId = opHeader.RequestId;
            }

            Logger.NetChannelRequestStarted(Node, this, request, requestId);

            var continuation = new PendingOperation(requestId);

            if (!PendingOperationsByRequestId.TryAdd(requestId, continuation))
            {
                throw new Exception("RequestId GUID is not unique");
            }

            var expiration = Task.Delay(TimeSpan.FromSeconds(_config.SendTimeoutSec), continuation.Expiration.Token);

            expiration.ContinueWith((_) =>
            {
                try
                {
                    PendingOperation dummy;
                    if (PendingOperationsByRequestId.TryRemove(continuation.RequestId, out dummy))
                    {
                        _requestsFailedMeter.Mark();
                        Logger.NetChannelRequestTimedOut(Node, this, request, requestId, _config.SendTimeoutSec * 1000);
                        if (!continuation.TCS.Task.IsCompleted)
                        {
                            continuation.TCS.SetException(new TimeoutException($"SendRequestAsync<{requestId}, {request}> has Expired after {_config.SendTimeoutSec} sec"));
                        }
                    }
                }
                catch {}
            });


            Send(request);

            return(continuation.TCS.Task);
        }