Class representing a STOMP FRAME. No validation is done here except a command string is required. All validations must be done in inheritors. This object is not immutable, BUT should be used as if it were.
        private async Task WriteFrameAsyncImpl(Frame frame, CancellationToken cancellationToken)
        {
#if TRACE
            Console.WriteLine(frame);
#endif
            if (frame.Command == StompCommands.Connect)
            {
                if (frame.Headers.Any(kvp => kvp.Key.Any(ch => ch == '\n' || ch == ':') || kvp.Value.Any(ch => ch == '\n')))
                    throw new InvalidDataException("Connect header names MUST not contain LF or ':' characters; Connected header values MUST not contain any LF.");

                await _writer.WriteAsync(frame.Command, cancellationToken);
                await _writer.WriteAsync(StompOctets.LineFeedByte, cancellationToken);

                await WriteConnectHeadersAsync(frame, cancellationToken);

                await _writer.WriteAsync(frame.BodyArray, cancellationToken);
                await _writer.WriteAsync(StompOctets.EndOfFrameByte, cancellationToken);
            }
            else
            {
                await _writer.WriteAsync(frame.Command, cancellationToken);
                await _writer.WriteAsync(StompOctets.LineFeedByte, cancellationToken);

                await WriteHeadersAsync(frame, cancellationToken);

                await _writer.WriteAsync(frame.BodyArray, cancellationToken);
                await _writer.WriteAsync(StompOctets.EndOfFrameByte, cancellationToken);
            }

            await _writer.FlushAsync(cancellationToken);
        }
        /// <summary>
        /// Transform a frame into a 'subclass' matching its command.
        /// These subclasses are: ReceiptFrame, MessageFrame, ErrorFrame, ConnectedFrame.
        /// </summary>
        /// <param name="frame">Frame to be interpreted.</param>
        /// <returns>A new frame which class matches its command type.</returns>
        public static Frame Interpret(Frame frame)
        {
            if (frame == null)
                throw new ArgumentNullException("frame");

            switch (frame.Command)
            {
                case StompCommands.Heartbeat:
                    return frame;
                case StompCommands.Receipt:
                    if (frame.BodyArray != Frame.EmptyBody)
                        throw new InvalidDataException("Receipt frame MUST NOT have a body.");
                    return new ReceiptFrame(frame.Headers);
                case StompCommands.Message:
                    return new MessageFrame(frame.Headers, frame.BodyArray);
                case StompCommands.Error:
                    return new ErrorFrame(frame.Headers, frame.BodyArray);
                case StompCommands.Connected:
                    if (frame.BodyArray != Frame.EmptyBody)
                        throw new InvalidDataException("Connected frame MUST NOT have a body.");
                    return new ConnectedFrame(frame.Headers);
                default:
                    throw new InvalidDataException(string.Format("'{0}' is not a valid STOMP server command.", frame.Command));
            }
        }
 public static Task WriteAsync(this IStompFrameWriter writer, Frame frame)
 {
     return writer.WriteAsync(frame, CancellationToken.None);
 }
        static bool AssertExpectedCommandFrame(Frame frame, string expectedFrameCommand)
        {
            // If error
            if (frame.Command == StompCommands.Error)
            {
                // Using the incoming frame, Interpret will create a new instance of a sub-class 
                // of Frame depending on the Command. If the frame is a HEARTBEAT, the same frame 
                // will be returned. Possible sub-classes: ConnectedFrame, ErrorFrame, MessageFrame,
                // ReceiptFrame.
                // Interpret throws exception if the incoming frame is malformed (not standard).
                ErrorFrame errFrame = StompInterpreter.Interpret(frame) as ErrorFrame;

                Console.WriteLine("ERROR RESPONSE");
                Console.WriteLine("Message : " + errFrame.Message);
                
                return false;
            }

            if (frame.Command != expectedFrameCommand)
            {
                Console.WriteLine("UNEXPECTED FRAME.");
                Console.WriteLine(frame.ToString());
                
                return false;
            }

            return true;
        }
Exemple #5
0
 public Task WriteAsync(Frame frame, CancellationToken cancellationToken)
 {
     return _frameWriter.WriteAsync(frame, cancellationToken);
 }
 public Task WriteAsync(Frame frame, CancellationToken cancellationToken)
 {
     return _serialTaskExecuter.Execute(() => _writer.WriteAsync(frame, cancellationToken), cancellationToken);
 }
 public async Task WriteAsync(Frame frame, CancellationToken cancellationToken)
 {
     //Make sure IO operations do not get executed on UI thread.
     await WriteFrameAsyncImpl(frame, cancellationToken).ConfigureAwait(false);
 }
 private async Task WriteHeadersAsync(Frame frame, CancellationToken cancellationToken)
 {
     foreach (var header in frame.Headers.Where(header => header.Value != null))
     {
         await WriteEscapedString(header.Key, cancellationToken);
         await _writer.WriteAsync(StompOctets.ColonByte, cancellationToken);
         await WriteEscapedString(header.Value, cancellationToken);
         await _writer.WriteAsync(StompOctets.LineFeedByte, cancellationToken);
     }
     await _writer.WriteAsync(StompOctets.LineFeedByte, cancellationToken);
 }
        /// <summary>
        /// Write a frame and wait for its receipt frame before returning.
        /// 
        /// This method does not add a receipt header if it is not included already.
        /// If the receipt header is not in the frame headers then it returns after sending the message.
        /// </summary>
        /// <param name="frame"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task WriteAsync(Frame frame, CancellationToken cancellationToken)
        {
            string receipt = frame.Command == StompCommands.Connect ? _connectReceiptHeader : frame.GetHeader(StompHeaders.Receipt);

            if (receipt == null)
            {
                await _writer.WriteAsync(frame, cancellationToken);
            }
            else
            {
                TaskCompletionSource<object> receiptWaiter = new TaskCompletionSource<object>();
                Task task = receiptWaiter.Task;
                _receiptWaiters[receipt] = receiptWaiter;

                do
                {
                    await _writer.WriteAsync(frame, cancellationToken);
                } while (await Task.WhenAny(task, Task.Delay(_retryInterval, cancellationToken)) != task 
                         && !task.IsFaulted && !task.IsCanceled);

                if (task.IsFaulted) throw task.Exception;
                if (task.IsCanceled) throw new OperationCanceledException(cancellationToken);
            }
        }
        private void OnNext(Frame frame)
        {
            string receiptId = 
                  frame.Command == StompCommands.Receipt ? frame.GetHeader(StompHeaders.ReceiptId)
                : frame.Command == StompCommands.Connected ? _connectReceiptHeader
                : null;

            if(receiptId != null)
            {
                TaskCompletionSource<object> tcs;
                if (_receiptWaiters.TryGetValue(receiptId, out tcs))
                {
                    tcs.TrySetResult(null);
                    _receiptWaiters.TryRemove(receiptId, out tcs);
                }
            }
        }