/// <summary> /// Initiates an asynchronous request/response transmission with a specific timeout. /// </summary> /// <param name="message">The request message.</param> /// <param name="timeout">The maximum time to wait for a response.</param> /// <param name="callback">The <see cref="AsyncCallback" /> delegate to be called when the operation completes (or <c>null</c>).</param> /// <param name="state">The application specific state (or <c>null</c>).</param> /// <returns>The <see cref="IAsyncResult" /> instance to be used to track the status of the operation.</returns> /// <remarks> /// <note> /// All successful calls to <see cref="BeginRequest(Message,TimeSpan,AsyncCallback,object)" /> must /// eventually be followed by a call to <see cref="EndRequest" />. /// </note> /// </remarks> public IAsyncResult BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, object state) { using (TimedLock.Lock(this)) { ThrowIfDisposedOrNotOpen(); ServiceModelHelper.ValidateTimeout(timeout); try { using (MemoryStream ms = new MemoryStream(payloadEstimator.EstimateNextBufferSize())) { WcfEnvelopeMsg requestMsg = new WcfEnvelopeMsg(); encoder.WriteMessage(message, ms); payloadEstimator.LastPayloadSize((int)ms.Length); requestMsg.Payload = new ArraySegment <byte>(ms.GetBuffer(), 0, (int)ms.Length); return(ChannelHost.Router.BeginQuery(ep, requestMsg, callback, state)); } } catch (Exception e) { throw ServiceModelHelper.GetCommunicationException(e); } } }
/// <summary> /// Intitiates an asynchronous operation to send a reply to the context request. /// </summary> /// <param name="context">The <see cref="LillTekRequestContext" /> context.</param> /// <param name="message">The reply <see cref="Message" />.</param> /// <param name="callback">The <see cref="AsyncCallback" /> 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 status of the operation.</returns> /// <remarks> /// <note> /// Every successful call to <see cref="BeginReply(LillTekRequestContext,Message,AsyncCallback,object)" /> must eventually be followed by /// a call to <see cref="EndReply" />. /// </note> /// </remarks> public IAsyncResult BeginReply(LillTekRequestContext context, Message message, AsyncCallback callback, object state) { AsyncResult arReply; // This operation is inherently asynchronous at the LillTek Messaging level // so we'll complete the operation immediately. using (MemoryStream ms = new MemoryStream(payloadEstimator.EstimateNextBufferSize())) { WcfEnvelopeMsg replyMsg = new WcfEnvelopeMsg(); encoder.WriteMessage(message, ms); payloadEstimator.LastPayloadSize((int)ms.Length); replyMsg.Payload = new ArraySegment <byte>(ms.GetBuffer(), 0, (int)ms.Length); using (TimedLock.Lock(this)) { if (pendingRequests.ContainsKey(context.MsgRequestContext.SessionID)) { pendingRequests.Remove(context.MsgRequestContext.SessionID); } context.MsgRequestContext.Reply(replyMsg); } } arReply = new AsyncResult(null, callback, state); arReply.Started(ServiceModelHelper.AsyncTrace); arReply.Notify(); return(arReply); }
//--------------------------------------------------------------------- // DuplexSession event handlers /// <summary> /// Handles messages received on the underlying LillTek <see cref="DuplexSession" /> session. /// </summary> /// <param name="session">The <see cref="DuplexSession" />.</param> /// <param name="msg">The received LillTek message.</param> private void OnSessionReceive(DuplexSession session, Msg msg) { WcfEnvelopeMsg envelopeMsg = msg as WcfEnvelopeMsg; if (envelopeMsg == null) { return; // Discard anything but WCF messages } Enqueue(listener.DecodeMessage(envelopeMsg)); }
/// <summary> /// Decodes the WCF <see cref="Message" /> encapsulated within a LillTek <see cref="WcfEnvelopeMsg" />. /// </summary> /// <param name="msg">The LillTek message.</param> /// <returns>The WCF <see cref="Message" />.</returns> /// <exception cref="CommunicationException">Thrown if the message could not be decoded.</exception> public Message DecodeMessage(WcfEnvelopeMsg msg) { using (BlockStream bs = new BlockStream((Block)msg.Payload)) { try { return(messageEncoderFactory.Encoder.ReadMessage(bs, ServiceModelHelper.MaxXmlHeaderSize)); } catch (Exception e) { throw ServiceModelHelper.GetCommunicationException(e); } } }
/// <summary> /// Synchronously sends a message on the output channel. /// </summary> /// <param name="message">The <see cref="Message" />.</param> /// <remarks> /// <note> /// This method does not guarantee delivery of the message. /// Messages can be silently dropped for reasons including lack of buffer /// space, network congestion, unavailable remote endpoint, etc. /// </note> /// </remarks> public void Send(Message message) { try { using (MemoryStream ms = new MemoryStream(payloadEstimator.EstimateNextBufferSize())) { WcfEnvelopeMsg envelopeMsg = new WcfEnvelopeMsg(); encoder.WriteMessage(message, ms); payloadEstimator.LastPayloadSize((int)ms.Length); envelopeMsg.Payload = new ArraySegment <byte>(ms.GetBuffer(), 0, (int)ms.Length); ChannelHost.Router.SendTo(ep, envelopeMsg); } } catch (Exception e) { throw ServiceModelHelper.GetCommunicationException(e); } }
/// <summary> /// Called when the message router receives a LillTek message. /// </summary> /// <param name="msg">The received message.</param> private void OnReceive(Msg msg) { try { if (sessionMode) { DuplexSessionMsg duplexMsg = msg as DuplexSessionMsg; // Handle client session connection attempts. if (duplexMsg != null) { OnSessionConnect(duplexMsg); return; } } else { // Handle encapsulated WCF messages. WcfEnvelopeMsg envelopeMsg = msg as WcfEnvelopeMsg; Message message; if (envelopeMsg == null) { return; // Discard non-envelope messages } message = DecodeMessage(envelopeMsg); // Let the derived listener decide what to do with the message. OnMessageReceived(message, envelopeMsg); } } catch (Exception e) { SysLog.LogException(e); } }
/// <summary> /// Called when the base class receives a LillTek envelope message with an /// encapsulated WCF message from the router. Non-session oriented derived /// classes must implement this to accept a new channel or route the message /// to an existing channel. /// </summary> /// <param name="message">The decoded WCF <see cref="Message" />.</param> /// <param name="msg">The received LillTek <see cref="Msg" />.</param> protected abstract void OnMessageReceived(Message message, WcfEnvelopeMsg msg);
/// <summary> /// Called when the base class receives a LillTek envelope message with an /// encapsulated WCF message from the router. Non-session oriented derived /// classes must implement this to accept a new channel or route the message /// to an existing channel. /// </summary> /// <param name="message">The decoded WCF <see cref="Message" />.</param> /// <param name="msg">The received LillTek <see cref="Msg" />.</param> /// <remarks> /// <para> /// This method takes different actions depending on whether there are /// any pending channel <b>WaitForMessage()</b> or <b>Receive()</b> requests. /// </para> /// <para> /// If there are pending message receive operations, then these will be completed /// and the message queued to the associated channel as is appropriate. /// </para> /// <para> /// Finally, if no pending message receive requests and the base class has a /// pending <b>WaitForChannel()</b> or <b>AcceptChannel()</b>, then the base class /// <see cref="LillTekChannelListener{IInputSessionChannel,InputSessionChannel}.OnChannelCreated" /> /// method will be called so that a new channel will be accepted. /// </para> /// <para> /// Finally, if there are no pending message receive requests or base channel /// channel accept related requests, the message will be queued internally. /// </para> /// </remarks> protected override void OnMessageReceived(Message message, WcfEnvelopeMsg msg) { InputChannel newChannel = null; if (base.State != CommunicationState.Opened) { return; } if (msg._SessionID != Guid.Empty) { return; // Reject messages that are part of a session } using (TimedLock.Lock(this)) { // Handle any pending channel Receive() operations first. if (receiveQueue.Count > 0) { AsyncResult <Message, InputChannel> arReceive; arReceive = receiveQueue.Dequeue(); arReceive.Result = message; arReceive.Notify(); return; } // Next, handle any pending channel WaitForMessage() operations. if (waitQueue.Count > 0) { AsyncResult <bool, InputChannel> arWait; // Queue the message to the input channel so it will assured // to be available when the WaitForMessage() completes and // the application calls Receive(). arWait = waitQueue.Dequeue(); arWait.Result = true; arWait.InternalState.Enqueue(message); arWait.Notify(); return; } // Queue the message. msgQueue.Enqueue(message); // Create new channel if there are pending channel accept // or wait operations. if (base.HasPendingChannelOperation) { newChannel = new InputChannel(this, new EndpointAddress(this.Uri)); AddChannel(newChannel); } } // Do this outside of the lock just to be safe if (newChannel != null) { base.OnChannelCreated(newChannel); } }
/// <summary> /// Called when the base class receives a LillTek envelope message with an /// encapsulated WCF message from the router. Non-session oriented derived /// classes must implement this to accept a new channel or route the message /// to an existing channel. /// </summary> /// <param name="message">The decoded WCF <see cref="Message" />.</param> /// <param name="msg">The received LillTek <see cref="Msg" />.</param> /// <remarks> /// <para> /// This method takes different actions depending on whether there are /// any pending channel <b>WaitForMessage()</b> or <b>Receive()</b> requests. /// </para> /// <para> /// If there are pending message receive operations, then these will be completed /// and the message queued to the associated channel as is appropriate. /// </para> /// <para> /// Finally, if no pending message receive requests and the base class has a /// pending <b>WaitForChannel()</b> or <b>AcceptChannel()</b>, then the base class /// <see cref="LillTekChannelListener{IInputSessionChannel,InputSessionChannel}.OnChannelCreated" /> /// method will be called so that a new channel will be accepted. /// </para> /// <para> /// Finally, if there are no pending message receive requests or base channel /// channel accept related requests, the message will be queued internally. /// </para> /// </remarks> protected override void OnMessageReceived(Message message, WcfEnvelopeMsg msg) { throw new InvalidOperationException(); }
/// <summary> /// Called when the base class receives a LillTek envelope message with an /// encapsulated WCF message from the router. Non-session oriented derived /// classes must implement this to accept a new channel or route the message /// to an existing channel. /// </summary> /// <param name="message">The decoded WCF <see cref="Message" />.</param> /// <param name="msg">The received LillTek <see cref="Msg" />.</param> protected override void OnMessageReceived(Message message, WcfEnvelopeMsg msg) { }
/// <summary> /// Called when the base class receives a LillTek envelope message with an /// encapsulated WCF message from the router. Non-session oriented derived /// classes must implement this to accept a new channel or route the message /// to an existing channel. /// </summary> /// <param name="message">The decoded WCF <see cref="Message" />.</param> /// <param name="msg">The received LillTek <see cref="Msg" />.</param> /// <remarks> /// <para> /// This method takes different actions depending on whether there are /// any pending channel <b>WaitForRequest()</b> or <b>ReceiveRequest()</b> requests. /// </para> /// <para> /// If there are pending request receive operations, then these will be completed /// and the rfequest information will be queued to the associated channel as is appropriate. /// </para> /// <para> /// Finally, if no pending request receive requests and the base class has a /// pending <b>WaitForChannel()</b> or <b>AcceptChannel()</b>, then the base class /// <see cref="LillTekChannelListener{IInputSessionChannel,InputSessionChannel}.OnChannelCreated" /> /// method will be called so that a new channel will be accepted. /// </para> /// <para> /// Finally, if there are no pending request receive requests or base channel /// channel accept related requests, the message will be queued internally. /// </para> /// </remarks> protected override void OnMessageReceived(Message message, WcfEnvelopeMsg msg) { RequestInfo requestInfo = new RequestInfo(message, msg.CreateRequestContext(), SysTime.Now + maxRequestQueueTime); ReplyChannel newChannel = null; if (base.State != CommunicationState.Opened) { return; } using (TimedLock.Lock(this)) { // Handle any pending channel ReceiveRequest() operations first. if (receiveQueue.Count > 0) { AsyncResult <RequestInfo, ReplyChannel> arReceive; arReceive = receiveQueue.Dequeue(); arReceive.Result = requestInfo; arReceive.Notify(); return; } // Next, handle any pending channel WaitForRequest() operations. if (waitQueue.Count > 0) { AsyncResult <bool, ReplyChannel> arWait; // Queue the request information to the input channel so it will assured // to be available when the WaitForRequest() completes and // the application calls ReceiveRequest(). arWait = waitQueue.Dequeue(); arWait.Result = true; arWait.InternalState.Enqueue(requestInfo); arWait.Notify(); return; } // Queue the request. requestQueue.Enqueue(requestInfo); // Create new channel if there are pending channel accept // or wait operations. if (base.HasPendingChannelOperation) { newChannel = new ReplyChannel(this, new EndpointAddress(this.Uri), base.MessageEncoder); AddChannel(newChannel); } } // Do this outside of the lock just to be safe if (newChannel != null) { base.OnChannelCreated(newChannel); } }