Exemple #1
0
        /// <summary>
        /// The managing <see cref="ISipAgent" /> is responsible for calling this
        /// method whenever it receives responses correlated to this transaction.
        /// </summary>
        /// <param name="transport">The source <see cref="ISipTransport" />.</param>
        /// <param name="response">The received <see cref="SipResponse" />.</param>
        public void OnResponse(ISipTransport transport, SipResponse response)
        {
            bool         callOnComplete       = false;
            bool         callOnProceeding     = false;
            bool         callOnInviteComplete = false;
            SipResponse  callbackMsg          = null;
            SipStatus    status = SipStatus.OK;
            SipCSeqValue vCSeq;

            this.transport = transport;

            try
            {
                response.SourceTransaction = this;

                using (TimedLock.Lock(agent))
                {
                    // Ignore messages without a sequence number
                    //
                    // $todo(jeff.lill): Probably should check the method too

                    vCSeq = response.GetHeader <SipCSeqValue>(SipHeader.CSeq);
                    if (vCSeq == null)
                    {
                        return;
                    }

                    // Handle state specific processing

                    switch (base.State)
                    {
                    default:
                    case SipTransactionState.Unknown:

                        SysLog.LogError("Unexpected SIP transaction state.");
                        SetState(SipTransactionState.Terminated);

                        // Setup to call the agent's completion method

                        callOnComplete = true;
                        callbackMsg    = null;
                        status         = SipStatus.Stack_ProtocolError;
                        return;

                    case SipTransactionState.InviteCalling:

                        if (!request.MatchCSeq(response))
                        {
                            return;         // Ignore responses whose CSeq header doesn't match the request
                        }
                        if (response.IsProvisional)
                        {
                            // Provisional response.

                            SetState(SipTransactionState.InviteProceeding);

                            // Setup to call the agent's proceeding method

                            callOnProceeding = true;
                            callbackMsg      = response;
                            status           = response.Status;
                            return;
                        }

                        if (response.IsNonSuccessFinal)
                        {
                            // Final response non-2xx response.  Generate and
                            // send the ACK request to the server to squelch
                            // any further responses and then enter the
                            // InviteCompleted state to absorb any responses
                            // that do make it through.

                            ackRequest = CreateAckRequest(request, response);
                            transport.Send(remoteEP, ackRequest);

                            SetState(SipTransactionState.InviteCompleted);

                            // Setup to call the agent's invite completed method

                            callOnInviteComplete = true;
                            callbackMsg          = response;
                            status = response.Status;
                            return;
                        }

                        // Must be a 2xx response.  Setup to call the agent's
                        // completed method and enter the terminated state
                        // without sending an ACK request.
                        //
                        // Note that the agent is required to do this as
                        // described in RFC 3261 on pages 128-129.

                        SetState(SipTransactionState.Terminated);

                        callOnInviteComplete = true;
                        callbackMsg          = response;
                        status = response.Status;
                        break;

                    case SipTransactionState.InviteProceeding:

                        if (!request.MatchCSeq(response))
                        {
                            return;         // Ignore responses whose CSeq header doesn't match the request
                        }
                        if (response.IsProvisional)
                        {
                            // Setup to call the agent's proceeding method

                            callOnProceeding = true;
                            callbackMsg      = response;
                            status           = response.Status;
                            return;
                        }

                        if (response.IsNonSuccessFinal)
                        {
                            // Final response non-2xx response.  Generate and
                            // send the ACK request to the server to squelch
                            // any further responses and then enter the
                            // InviteCompleted state to absorb any responses
                            // that do make it through.

                            // $todo(jeff.lill):
                            //
                            // I need to figure out a way to
                            // map to the dialog so that it
                            // can generate the ACK rather than
                            // doing this locally.

                            ackRequest = CreateAckRequest(request, response);
                            transport.Send(remoteEP, ackRequest);

                            SetState(SipTransactionState.InviteCompleted);

                            // Setup to call the agent's invite completed method

                            callOnInviteComplete = true;
                            callbackMsg          = response;
                            status = response.Status;
                            return;
                        }

                        // Must be a 2xx response.  Setup to call the agent's
                        // completed method and enter the terminated state
                        // without sending an ACK request.
                        //
                        // Note that the agent is required to do this as
                        // described in RFC 3261 on pages 128-129.

                        SetState(SipTransactionState.Terminated);

                        callOnInviteComplete = true;
                        callbackMsg          = response;
                        status = response.Status;
                        break;

                    case SipTransactionState.InviteCompleted:

                        // Retransmit the ACK if we get another final response

                        if (response.IsFinal)
                        {
                            transport.Send(remoteEP, ackRequest);
                        }

                        break;

                    case SipTransactionState.Trying:

                        if (!request.MatchCSeq(response))
                        {
                            return;         // Ignore responses whose CSeq header doesn't match the request
                        }
                        if (response.IsProvisional)
                        {
                            // Provisional response.

                            SetState(SipTransactionState.Proceeding);

                            // Setup to call the agent's proceeding method

                            callOnProceeding = true;
                            callbackMsg      = response;
                            status           = response.Status;
                            return;
                        }
                        else
                        {
                            // Final response

                            SetState(SipTransactionState.Completed);

                            // Setup to call the agent's completion method

                            callOnComplete = true;
                            callbackMsg    = response;
                            status         = response.Status;
                            return;
                        }

                    case SipTransactionState.Proceeding:

                        if (!request.MatchCSeq(response))
                        {
                            return;         // Ignore responses whose CSeq header doesn't match the request
                        }
                        if (response.IsProvisional)
                        {
                            // Setup to call the agent's proceeding method

                            callOnProceeding = true;
                            callbackMsg      = response;
                            status           = response.Status;
                            return;
                        }

                        // Final response.

                        SetState(SipTransactionState.Completed);

                        // Setup to call the agent's completion method

                        callOnComplete = true;
                        callbackMsg    = response;
                        status         = response.Status;
                        return;

                    case SipTransactionState.Completed:

                        break;

                    case SipTransactionState.Terminated:

                        break;
                    }
                }
            }
            finally
            {
                // Handle the agent callbacks outside of the lock to avoid
                // deadlock issues.

                if (callOnProceeding)
                {
                    agent.OnProceeding(this, callbackMsg);
                }

                if (callOnComplete)
                {
                    agent.OnComplete(this, status, callbackMsg);
                }

                if (callOnInviteComplete)
                {
                    agent.OnInviteComplete(this, status, response);
                }
            }
        }