예제 #1
0
        //---------------------------------------------------------------------
        // Event handling methods called by SipClientTransaction.

        /// <summary>
        /// Handles messages received by a transport to be processed by this agent.
        /// </summary>
        /// <param name="transport">The source transport.</param>
        /// <param name="message">The received message.</param>
        public void OnReceive(ISipTransport transport, SipMessage message)
        {
            SipResponse          response = (SipResponse)message;
            SipClientTransaction transaction;
            string transactionID;

            if (response == null)
            {
                return;     // Ignore any requests
            }
            // Route the message to the correct transaction.

            if (!response.TryGetTransactionID(out transactionID))
            {
                return;
            }

            using (TimedLock.Lock(this))
            {
                if (!transactions.TryGetValue(transactionID, out transaction))
                {
                    // The response doesn't map to an existing transaction.
                    // We're going to pass this to the core's OnUncorrelatedResponse()
                    // method.

                    core.OnUncorrelatedResponse(this, response);
                    return;
                }
            }

            transaction.OnResponse(transport, response);
        }
예제 #2
0
 public void Route(ISipTransport transport, SipMessage message)
 {
     lock (syncLock)
     {
         queue.Enqueue(message);
         recvEvt.Set();
     }
 }
예제 #3
0
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="agent">The <see cref="ISipAgent" /> that owns this transaction.</param>
 /// <param name="id">The globally unique transaction ID.</param>
 /// <param name="transport">The <see cref="ISipTransport" /> to be used for this transaction.</param>
 /// <remarks>
 /// The timers <see cref="TimerA" /> through <see cref="TimerK" /> are initialized
 /// with the correct <see cref="PolledTimer.Interval" /> values for the given transport.
 /// These timers will then need to be <see cref="PolledTimer.Reset()" /> before they
 /// are actually used so they will be scheduled to fire at the correct time.
 /// </remarks>
 protected SipTransaction(ISipAgent agent, string id, ISipTransport transport)
 {
     this.agent      = agent;
     this.id         = id;
     this.state      = SipTransactionState.Unknown;
     this.transport  = transport;
     this.baseTimers = transport.Settings.BaseTimers;
     this.agentState = null;
 }
예제 #4
0
        private NetworkBinding remoteEP;                    // The message source endpoint

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="isRequest"><c>true</c> if the message is a SIP request, false for a response</param>
        /// <param name="sipVersion">The SIP version string (or <c>null</c>).</param>
        internal SipMessage(bool isRequest, string sipVersion)
        {
            this.isRequest         = isRequest;
            this.sipVersion        = sipVersion == null ? "SIP/2.0" : sipVersion.ToUpper();
            this.headers           = new SipHeaderCollection();
            this.contents          = emptyPayload;
            this.sourceTransaction = null;
            this.sourceTransport   = null;
            this.remoteEP          = null;
        }
예제 #5
0
 /// <summary>
 /// Routes a <see cref="SipMessage" /> received by an <see cref="ISipTransport" /> to the <see cref="ISipAgent" />
 /// instance that needs to handle it.
 /// </summary>
 /// <param name="transport">The <see cref="ISipTransport" /> that received the message.</param>
 /// <param name="message">The <see cref="SipMessage" /> received by the transport.</param>
 public void Route(ISipTransport transport, SipMessage message)
 {
     if (message is SipRequest && agent is SipServerAgent)
     {
         agent.OnReceive(transport, message);
     }
     else if (message is SipResponse && agent is SipClientAgent)
     {
         agent.OnReceive(transport, message);
     }
 }
예제 #6
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="agent">The <see cref="ISipAgent" /> that owns this transaction.</param>
        /// <param name="id">The globally unique transaction ID.</param>
        /// <param name="transport">The <see cref="ISipTransport" /> to be used for this transaction.</param>
        /// <param name="ttd">(Time-to-die) The time (SYS) where the transaction should terminate itself regardless of its current state.</param>
        public SipServerTransaction(SipServerAgent agent, string id, ISipTransport transport, DateTime ttd)
            : base(agent, id, transport)
        {
            this.ttd       = ttd;
            this.agent     = (SipServerAgent)agent;
            this.transport = transport;
            this.isUdp     = !transport.IsStreaming;

            this.request             = null;
            this.remoteEP            = null;
            this.provisionalResponse = null;
            this.finalResponse       = null;
        }
예제 #7
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="agent">The <see cref="ISipAgent" /> that owns this transaction.</param>
        /// <param name="request">The <see cref="SipRequest" /> initiating the transaction.</param>
        /// <param name="id">The globally unique transaction ID.</param>
        /// <param name="transport">The <see cref="ISipTransport" /> to be used for this tranaaction.</param>
        /// <param name="remoteEP">The server side's <see cref="NetworkBinding" />.</param>
        public SipClientTransaction(SipClientAgent agent, SipRequest request, string id, ISipTransport transport, NetworkBinding remoteEP)
            : base(agent, id, transport)
        {
            if (request.Method == SipMethod.Ack)
            {
                throw new SipException("Client transactions cannot be initiated with an ACK request.");
            }

            this.agent      = (SipClientAgent)agent;
            this.transport  = transport;
            this.isUdp      = !transport.IsStreaming;
            this.request    = request;
            this.ackRequest = null;
            this.remoteEP   = remoteEP;
        }
예제 #8
0
        //---------------------------------------------------------------------
        // ISipMessageRouter implementation

        /// <summary>
        /// Routes a <see cref="SipMessage" /> received by an <see cref="ISipTransport" /> to the <see cref="ISipAgent" />
        /// instance that needs to handle it.
        /// </summary>
        /// <param name="transport">The <see cref="ISipTransport" /> that received the message.</param>
        /// <param name="message">The <see cref="SipMessage" /> received by the transport.</param>
        public void Route(ISipTransport transport, SipMessage message)
        {
            // Routing is easy:
            //
            //      Servers get the requests
            //      Clients get the responses.

            if (message is SipRequest)
            {
                serverAgent.OnReceive(transport, message);
            }
            else
            {
                clientAgent.OnReceive(transport, message);
            }
        }
예제 #9
0
        /// <summary>
        /// Performs a deep copy of the internal properties of the message passed
        /// to this instance.
        /// </summary>
        /// <param name="message">The message to be copied.</param>
        internal void CopyFrom(SipMessage message)
        {
            this.isRequest         = message.isRequest;
            this.sipVersion        = message.sipVersion;
            this.sourceTransaction = null;
            this.sourceTransport   = message.sourceTransport;
            this.remoteEP          = message.remoteEP;

            this.contents = null;
            if (message.contents != null)
            {
                this.contents = new byte[message.contents.Length];
                Array.Copy(message.contents, this.contents, message.contents.Length);
            }

            foreach (SipHeader header in message.headers.Values)
            {
                this.headers.Add(header.Name, header.Clone());
            }
        }
예제 #10
0
 public void Route(ISipTransport transport, SipMessage message)
 {
     received = message;
     recvEvt.Set();
 }
예제 #11
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);
                }
            }
        }