/// <summary>
        /// Sends specified request to flow remote end point.
        /// </summary>
        /// <param name="request">SIP request to send.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>request</b> is null reference.</exception>
        public void Send(SIP_Request request)
        {
            lock (m_pLock){
                if (m_IsDisposed)
                {
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                if (request == null)
                {
                    throw new ArgumentNullException("request");
                }

                SendInternal(request.ToByteData());
            }
        }
        /// <summary>
        /// Processes specified request through this transaction.
        /// </summary>
        /// <param name="flow">SIP data flow.</param>
        /// <param name="request">SIP request.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        internal void ProcessRequest(SIP_Flow flow, SIP_Request request)
        {
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            lock (this.SyncRoot){
                if (this.State == SIP_TransactionState.Disposed)
                {
                    return;
                }

                try{
                    // Log
                    if (this.Stack.Logger != null)
                    {
                        byte[] requestData = request.ToByteData();

                        this.Stack.Logger.AddRead(
                            Guid.NewGuid().ToString(),
                            null,
                            0,
                            "Request [transactionID='" + this.ID + "'; method='" + request.RequestLine.Method + "'; cseq='" + request.CSeq.SequenceNumber + "'; " +
                            "transport='" + flow.Transport + "'; size='" + requestData.Length + "'; received '" + flow.LocalEP + "' <- '" + flow.RemoteEP + "'.",
                            flow.LocalEP,
                            flow.RemoteEP,
                            requestData
                            );
                    }

                    #region INVITE

                    if (this.Method == SIP_Methods.INVITE)
                    {
                        #region INVITE

                        if (request.RequestLine.Method == SIP_Methods.INVITE)
                        {
                            if (this.State == SIP_TransactionState.Proceeding)
                            {
                                /* RFC 3261 17.2.1.
                                 *  If a request retransmission is received while in the "Proceeding" state, the most recent provisional
                                 *  response that was received from the TU MUST be passed to the transport layer for retransmission.
                                 */
                                SIP_Response response = this.LastProvisionalResponse;
                                if (response != null)
                                {
                                    this.Stack.TransportLayer.SendResponse(this, response);
                                }
                            }
                            else if (this.State == SIP_TransactionState.Completed)
                            {
                                /* RFC 3261 17.2.1.
                                 *  While in the "Completed" state, if a request retransmission is received, the server SHOULD
                                 *  pass the response to the transport for retransmission.
                                 */
                                this.Stack.TransportLayer.SendResponse(this, this.FinalResponse);
                            }
                        }

                        #endregion

                        #region ACK

                        else if (request.RequestLine.Method == SIP_Methods.ACK)
                        {
                            #region Accepeted

                            if (this.State == SIP_TransactionState.Accpeted)
                            {
                            }

                            #endregion

                            #region Completed

                            else if (this.State == SIP_TransactionState.Completed)
                            {
                                /* RFC 3261 17.2.1
                                 *  If an ACK is received while the server transaction is in the "Completed" state, the server transaction
                                 *  MUST transition to the "Confirmed" state.  As Timer G is ignored in this state, any retransmissions of the
                                 *  response will cease.
                                 *
                                 *  When this state is entered, timer I is set to fire in T4 seconds for unreliable transports,
                                 *  and zero seconds for reliable transports.
                                 */

                                SetState(SIP_TransactionState.Confirmed);

                                // Stop timers G,H
                                if (m_pTimerG != null)
                                {
                                    m_pTimerG.Dispose();
                                    m_pTimerG = null;

                                    // Log
                                    if (this.Stack.Logger != null)
                                    {
                                        this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer G(INVITE response(3xx - 6xx) retransmission) stopped.");
                                    }
                                }
                                if (m_pTimerH != null)
                                {
                                    m_pTimerH.Dispose();
                                    m_pTimerH = null;

                                    // Log
                                    if (this.Stack.Logger != null)
                                    {
                                        this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer H(INVITE ACK wait) stopped.");
                                    }
                                }

                                // Start timer I.
                                m_pTimerI          = new TimerEx((flow.IsReliable ? 0 : SIP_TimerConstants.T4), false);
                                m_pTimerI.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimerI_Elapsed);
                                // Log
                                if (this.Stack.Logger != null)
                                {
                                    this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] timer I(INVITE ACK retransission wait) started, will trigger after " + m_pTimerI.Interval + ".");
                                }
                                m_pTimerI.Enabled = true;
                            }

                            #endregion
                        }

                        #endregion
                    }

                    #endregion

                    #region Non-INVITE

                    else
                    {
                        // Non-INVITE transaction may have only request retransmission requests.
                        if (this.Method == request.RequestLine.Method)
                        {
                            if (this.State == SIP_TransactionState.Proceeding)
                            {
                                /* RFC 3261 17.2.2.
                                 *  If a retransmission of the request is received while in the "Proceeding" state, the most
                                 *  recently sent provisional response MUST be passed to the transport layer for retransmission.
                                 */
                                this.Stack.TransportLayer.SendResponse(this, this.LastProvisionalResponse);
                            }
                            else if (this.State == SIP_TransactionState.Completed)
                            {
                                /* RFC 3261 17.2.2.
                                 *  While in the "Completed" state, the server transaction MUST pass the final response to the transport
                                 *  layer for retransmission whenever a retransmission of the request is received.
                                 */
                                this.Stack.TransportLayer.SendResponse(this, this.FinalResponse);
                            }
                        }
                    }

                    #endregion
                }
                catch (SIP_TransportException x) {
                    // Log
                    if (this.Stack.Logger != null)
                    {
                        this.Stack.Logger.AddText(this.ID, "Transaction [branch='" + this.ID + "';method='" + this.Method + "';IsServer=true] transport exception: " + x.Message);
                    }

                    OnTransportError(x);
                }
            }
        }
        /// <summary>
        /// Starts sending request.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        /// <exception cref="InvalidOperationException">Is raised when <b>Start</b> method has alredy called.</exception>
        /// <exception cref="SIP_TransportException">Is raised when no transport hop(s) for request.</exception>
        public void Start()
        {
            lock (m_pLock){
                if (m_State == SIP_RequestSenderState.Disposed)
                {
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                if (m_IsStarted)
                {
                    throw new InvalidOperationException("Start method has been already called.");
                }
                m_IsStarted = true;
                m_State     = SIP_RequestSenderState.Starting;

                // Start may take so, process it on thread pool.
                ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object state){
                    lock (m_pLock){
                        if (m_State == SIP_RequestSenderState.Disposed)
                        {
                            return;
                        }

                        /* RFC 3261 8.1.2 Sending the Request
                         *  The destination for the request is then computed.  Unless there is
                         *  local policy specifying otherwise, the destination MUST be determined
                         *  by applying the DNS procedures described in [4] as follows.  If the
                         *  first element in the route set indicated a strict router (resulting
                         *  in forming the request as described in Section 12.2.1.1), the
                         *  procedures MUST be applied to the Request-URI of the request.
                         *  Otherwise, the procedures are applied to the first Route header field
                         *  value in the request (if one exists), or to the request's Request-URI
                         *  if there is no Route header field present.  These procedures yield an
                         *  ordered set of address, port, and transports to attempt.  Independent
                         *  of which URI is used as input to the procedures of [4], if the
                         *  Request-URI specifies a SIPS resource, the UAC MUST follow the
                         *  procedures of [4] as if the input URI were a SIPS URI.
                         *
                         *  The UAC SHOULD follow the procedures defined in [4] for stateful
                         *  elements, trying each address until a server is contacted.  Each try
                         *  constitutes a new transaction, and therefore each carries a different
                         *  topmost Via header field value with a new branch parameter.
                         *  Furthermore, the transport value in the Via header field is set to
                         *  whatever transport was determined for the target server.
                         */

                        // We never use strict, only loose route.
                        bool isStrictRoute = false;

                        SIP_Uri uri = null;
                        if (isStrictRoute)
                        {
                            uri = (SIP_Uri)m_pRequest.RequestLine.Uri;
                        }
                        else if (m_pRequest.Route.GetTopMostValue() != null)
                        {
                            uri = (SIP_Uri)m_pRequest.Route.GetTopMostValue().Address.Uri;
                        }
                        else
                        {
                            uri = (SIP_Uri)m_pRequest.RequestLine.Uri;
                        }

                        try{
                            // Queue hops.
                            foreach (SIP_Hop hop in m_pStack.GetHops(uri, m_pRequest.ToByteData().Length, ((SIP_Uri)m_pRequest.RequestLine.Uri).IsSecure))
                            {
                                m_pHops.Enqueue(hop);
                            }
                        }
                        catch (Exception x) {
                            OnTransportError(new SIP_TransportException("SIP hops resolving failed '" + x.Message + "'."));
                            OnCompleted();

                            return;
                        }

                        if (m_pHops.Count == 0)
                        {
                            OnTransportError(new SIP_TransportException("No target hops resolved for '" + uri + "'."));
                            OnCompleted();
                        }
                        else
                        {
                            m_State = SIP_RequestSenderState.Started;

                            try{
                                if (m_pFlow != null)
                                {
                                    SendToFlow(m_pFlow, m_pRequest.Copy());

                                    return;
                                }
                            }
                            catch {
                                // Sending to specified flow failed, probably disposed, just try send to first hop.
                            }

                            SendToNextHop();
                        }
                    }
                }));
            }
        }