/// <summary> /// Constuctor. /// </summary> /// <param name="status">The <see cref="SipStatus" />.</param> /// <param name="response">The received <see cref="SipResponse" /> (or <c>null</c>).</param> /// <param name="transaction">The <see cref="SipClientTransaction" />.</param> /// <param name="dialog">The associated <see cref="SipDialog" /> (or <c>null</c>).</param> /// <param name="agent">The <see cref="SipClientAgent" /> that processed the response.</param> /// <param name="core">The <see cref="SipCore" /> that raised the event.</param> internal SipResponseEventArgs(SipStatus status, SipResponse response, SipClientTransaction transaction, SipDialog dialog, SipClientAgent agent, SipCore core) { this.Status = status; this.Response = response; this.Transaction = transaction; this.Dialog = dialog; this.Agent = agent; this.Core = core; }
/// <summary> /// Called when an INVITE transaction completes. /// </summary> /// <param name="transaction">The source <see cref="SipClientTransaction" />.</param> /// <param name="status">The completion status.</param> /// <param name="response">The final response (or <c>null</c>).</param> /// <remarks> /// <para> /// The <paramref name="response"/> parameter will be passed as <c>null</c> if /// the transaction was completed without receiving a final message (such /// as a timeout). In this case, the agent should look to the <paramref name="status"/> /// property for the final disposition of the transaction. /// </para> /// <para> /// This method also handles the resubmission of the request with additional /// authentication information if necessary. /// </para> /// <para> /// The method may create a custom ACK <see cref="SipResponse" /> to be delivered /// back to the server by saving the response in the <paramref name="response"/> parameter. /// Otherwise, if this value is left as <c>null</c>, the client transaction will /// generate a default ACK response and send it. /// </para> /// </remarks> internal void OnInviteComplete(SipClientTransaction transaction, SipStatus status, SipResponse response) { var arClient = (ClientAsyncResult)transaction.AgentState; var args = new SipResponseEventArgs(status, response, transaction, arClient.Dialog, this, this.core); if (response == null) { // The operation has completed without receiving a final response (probably // due to a time out or some kind of transport related problem). arClient.SipResult = new SipResult(arClient.Request, arClient.Dialog, this, status); arClient.Notify(); core.OnResponseReceived(args); core.OnInviteFailed(args, status); return; } // We have the final response. Compute the dialog ID from the response's // Call-ID, and To/From tags and assign it to the dialog. arClient.Dialog.ID = SipDialog.GetDialogID(response); // Call the core's OnInviteConfirmed() method so it can perform // any dialog specific activities. if (response.IsSuccess) { core.OnInviteConfirmed(args); } else { core.OnInviteFailed(args, status); } // Signal completion of the async operation. arClient.SipResult = new SipResult(arClient.Request, arClient.Dialog, this, response); arClient.Notify(); }
/// <summary> /// Called when a non-INVITE transaction completes. /// </summary> /// <param name="transaction">The source <see cref="SipClientTransaction" />.</param> /// <param name="status">The completion status.</param> /// <param name="response">The final response (or <c>null</c>).</param> /// <remarks> /// <para> /// The <paramref name="response"/> parameter will be passed as <c>null</c> if /// the transaction was completed without receiving a final message (such /// as a timeout). In this case, the agent should look to the <paramref name="status"/> /// property for the final disposition of the transaction. /// </para> /// <para> /// This method also handles the resubmission of the request with additional /// authentication information if necessary. /// </para> /// </remarks> internal void OnComplete(SipClientTransaction transaction, SipStatus status, SipResponse response) { var arClient = (ClientAsyncResult)transaction.AgentState; if (response == null) { // The operation has completed without receiving a final response (probably // due to a time out or some kind of transport related problem). arClient.SipResult = new SipResult(arClient.Request, arClient.Dialog, this, status); arClient.Notify(); core.OnResponseReceived(new SipResponseEventArgs(status, response, transaction, arClient.Dialog, this, this.core)); return; } // We have the final response. arClient.SipResult = new SipResult(arClient.Request, arClient.Dialog, this, response); arClient.Notify(); core.OnResponseReceived(new SipResponseEventArgs(response.Status, response, transaction, arClient.Dialog, this, this.core)); }
/// <summary> /// Called when a transaction receives a 1xx response. /// </summary> /// <param name="transaction">The source <see cref="SipClientTransaction" />.</param> /// <param name="response">The <see cref="SipResponse" /> received.</param> internal void OnProceeding(SipClientTransaction transaction, SipResponse response) { var arClient = (ClientAsyncResult)transaction.AgentState; core.OnResponseReceived(new SipResponseEventArgs(response.Status, response, transaction, arClient.Dialog, this, this.core)); }
/// <summary> /// Initiates an asynchronous SIP request transaction. /// </summary> /// <param name="request">The <see cref="SipRequest" /> to be submitted.</param> /// <param name="dialog">The <see cref="SipDialog" /> for requests that initiate a dialog (or <c>null</c>).</param> /// <param name="callback">The delegate to be called when the operation completes (or <c>null</c>).</param> /// <param name="state">Application defined state (or <c>null</c>).</param> /// <returns>The <see cref="IAsyncResult" /> to be used to track the operation's progress.</returns> /// <remarks> /// <para> /// All requests to <see cref="BeginRequest(SipRequest,SipDialog,AsyncCallback,object)" /> must be matched with a /// call to <see cref="EndRequest" />. /// </para> /// <note> /// This method adds reasonable <b>Call-ID</b> and <b>CSeq</b> headers to the request if these /// headers are not already present. /// </note> /// </remarks> public IAsyncResult BeginRequest(SipRequest request, SipDialog dialog, AsyncCallback callback, object state) { ClientAsyncResult arClient = new ClientAsyncResult(request, dialog, callback, state); SipValue viaValue; SipCSeqValue vCSeq; string transactionID; SipClientTransaction transaction; ISipTransport transport; NetworkBinding remoteEP; if (dialog != null && request.Method != SipMethod.Invite) { throw new InvalidOperationException("Dialogs may be created only for INVITE requests."); } arClient.Dialog = dialog; transport = router.SelectTransport(this, request, out remoteEP); if (transport == null) { throw new SipException("No approriate transport is available."); } // Initialize the request's Via header and transaction ID as necessary. transactionID = SipHelper.GenerateBranchID(); viaValue = new SipValue(string.Format("SIP/2.0/{0} {1}", transport.Name, transport.Settings.ExternalBinding.Address)); viaValue["branch"] = transactionID; viaValue["rport"] = string.Empty; request.PrependHeader(SipHeader.Via, viaValue); // Initialize common headers as necessary if (!request.ContainsHeader(SipHeader.CallID)) { request.AddHeader(SipHeader.CallID, SipHelper.GenerateCallID()); } vCSeq = request.GetHeader <SipCSeqValue>(SipHeader.CSeq); if (vCSeq == null) { vCSeq = new SipCSeqValue(SipHelper.GenCSeq(), request.MethodText); request.AddHeader(SipHeader.CSeq, vCSeq); } // Initialize the transaction transaction = new SipClientTransaction(this, request, transactionID, transport, remoteEP); transaction.AgentState = arClient; // Handle initial dialog INVITE specific initialization if (dialog != null && request.Method == SipMethod.Invite && dialog.State == SipDialogState.Waiting) { // Client-side dialogs need to know the transaction so // they'll be able to send the confirming ACK. dialog.InitiatingTransaction = transaction; // Dialogs need to know about the sequence number used in INVITE requests so // that the ACK can be generated with the same sequence number. dialog.AckCSeq = vCSeq.Number; // The dialog has been intialized enough to be added to the core's // early dialog table. core.AddEarlyDialog(dialog); } // Start the transaction using (TimedLock.Lock(this)) { transactions.Add(transactionID, transaction); } transaction.Start(); arClient.Started(); return(arClient); }