/// <summary> /// Sends a request message to the client over the open network stream and stores the request to the list of unfinished requests. /// </summary> /// <param name="Message">Message to send.</param> /// <param name="Context">Caller's defined context to store information required for later response processing.</param> /// <returns>true if the connection to the client should remain open, false otherwise.</returns> public async Task <bool> SendMessageAndSaveUnfinishedRequestAsync(Message Message, object Context) { log.Trace("()"); bool res = false; UnfinishedRequest unfinishedRequest = new UnfinishedRequest(Message, Context); if (AddUnfinishedRequest(unfinishedRequest)) { res = await SendMessageInternalAsync(Message); if (res) { // If the message was sent successfully to the target, we close the connection only in case it was a protocol violation error response. if (Message.MessageTypeCase == Message.MessageTypeOneofCase.Response) { res = Message.Response.Status != Status.ErrorProtocolViolation; } } else { RemoveUnfinishedRequest(unfinishedRequest.RequestMessage.Id); } } log.Trace("(-):{0}", res); return(res); }
/// <summary> /// Finds an unfinished request message by its ID and removes it from the list. /// </summary> /// <param name="Id">Identifier of the message to find.</param> /// <returns>Unfinished request with the given ID, or null if no such request is in the list.</returns> public UnfinishedRequest GetAndRemoveUnfinishedRequest(uint Id) { log.Trace("(Id:{0})", Id); UnfinishedRequest res = null; lock (unfinishedRequestsLock) { if (unfinishedRequests.TryGetValue(Id, out res)) { unfinishedRequests.Remove(Id); } } log.Trace("(-):{0},unfinishedRequests.Count={1}", res != null ? "UnfinishedRequest" : "null", unfinishedRequests.Count); return(res); }
/// <summary> /// Adds unfinished request to the list of unfinished requests. /// </summary> /// <param name="Request">Request to add to the list.</param> /// <returns>true if the function succeeds, false if the number of unfinished requests is over the limit.</returns> public bool AddUnfinishedRequest(UnfinishedRequest Request) { log.Trace("(Request.RequestMessage.Id:{0})", Request.RequestMessage.Id); bool res = false; lock (unfinishedRequestsLock) { if (unfinishedRequests.Count < MaxUnfinishedRequests) { unfinishedRequests.Add(Request.RequestMessage.Id, Request); res = true; } } log.Trace("(-):{0},unfinishedRequests.Count={1}", res, unfinishedRequests.Count); return(res); }
/// <summary> /// Handles situation when the callee replied to the incoming call notification request. /// </summary> /// <param name="ResponseMessage">Full response message from the callee.</param> /// <param name="Request">Unfinished call request message of the caller that corresponds to the response message.</param> /// <returns></returns> public async Task <bool> CalleeRepliedToIncomingCallNotification(Message ResponseMessage, UnfinishedRequest Request) { log.Trace("()"); bool res = false; bool destroyRelay = false; Client clientToSendMessage = null; Message messageToSend = null; await lockObject.WaitAsync(); if (status == RelayConnectionStatus.WaitingForCalleeResponse) { CancelTimeoutTimerLocked(); // The caller is still connected and waiting for an answer to its call request. if (ResponseMessage.Response.Status == Status.Ok) { // The callee is now expected to connect to clAppService with its token. // We need to inform caller that the callee accepted the call. // This is option 4) from ProcessMessageCallIdentityApplicationServiceRequestAsync. messageToSend = caller.MessageBuilder.CreateCallIdentityApplicationServiceResponse(pendingMessage, callerToken.ToByteArray()); clientToSendMessage = caller; pendingMessage = null; caller = null; callee = null; status = RelayConnectionStatus.WaitingForFirstInitMessage; log.Debug("Relay '{0}' status has been changed to {1}.", id, status); /// Install timeoutTimer to expire if the first client does not connect to clAppService port /// and send its initialization message within reasonable time. timeoutTimer = new Timer(TimeoutCallback, RelayConnectionStatus.WaitingForFirstInitMessage, FirstAppServiceInitializationMessageDelayMaxSeconds * 1000, Timeout.Infinite); res = true; } else { // The callee rejected the call or reported other error. // These are options 3) and 2) from ProcessMessageCallIdentityApplicationServiceRequestAsync. if (ResponseMessage.Response.Status == Status.ErrorRejected) { log.Debug("Callee ID '0x{0:X16}' rejected the call from caller identity ID '0x{1:X16}', relay '{2}'.", callee.Id, caller.Id, id); messageToSend = caller.MessageBuilder.CreateErrorRejectedResponse(pendingMessage); } else { log.Warn("Callee ID '0x{0:X16}' sent error response '{1}' for call request from caller identity ID '0x{2:X16}', relay '{3}'.", callee.Id, ResponseMessage.Response.Status, caller.Id, id); messageToSend = caller.MessageBuilder.CreateErrorNotAvailableResponse(pendingMessage); } clientToSendMessage = caller; destroyRelay = true; } } else { // The relay has probably been destroyed, or something bad happened to it. // We take no action here regardless of what the callee's response is. // If it rejected the call, there is nothing to be done since we do not have // any connection to the caller anymore. log.Debug("Relay status is {0}, nothing to be done.", status); } lockObject.Release(); if (messageToSend != null) { if (await clientToSendMessage.SendMessageAsync(messageToSend)) { log.Debug("Response to call initiation request sent to the caller ID '0x{0:X16}'.", clientToSendMessage.Id); } else { log.Debug("Unable to reponse to call initiation request to the caller ID '0x{0:X16}'.", clientToSendMessage.Id); } } if (destroyRelay) { Server serverComponent = (Server)Base.ComponentDictionary["Network.Server"]; ClientList clientList = serverComponent.GetClientList(); await clientList.DestroyNetworkRelay(this); } log.Trace("(-):{0}", res); return(res); }