//--------------------------------------------------------------------- // 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); }
public void Route(ISipTransport transport, SipMessage message) { lock (syncLock) { queue.Enqueue(message); recvEvt.Set(); } }
/// <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; }
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; }
/// <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); } }
/// <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; }
/// <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; }
//--------------------------------------------------------------------- // 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); } }
/// <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()); } }
public void Route(ISipTransport transport, SipMessage message) { received = message; recvEvt.Set(); }
/// <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); } } }