/// <summary>
 /// Initializes a new instance.
 /// </summary>
 public StompErrorFrameException(StompFrame frame) : base(frame, GetErrorMessage(frame))
 {
 }
 /// <summary>
 /// Extracts the message from the error frame.
 /// </summary>
 /// <param name="frame"></param>
 /// <returns></returns>
 static string GetErrorMessage(StompFrame frame)
 {
     return(frame.GetHeaderValue("message") ?? GetErrorBody(frame));
 }
예제 #3
0
 /// <summary>
 /// Sends a single STOMP frame to the transport.
 /// </summary>
 /// <param name="frame"></param>
 /// <param name="cancellationToken"></param>
 /// <returns></returns>
 public abstract ValueTask SendAsync(StompFrame frame, CancellationToken cancellationToken);
예제 #4
0
        /// <summary>
        /// Attempts to parse a single frame from the given sequence.
        /// </summary>
        /// <param name="sequence"></param>
        /// <param name="frame"></param>
        /// <returns></returns>
        public bool TryReadFrame(ref SequenceReader <byte> sequence, out StompFrame frame)
        {
            frame = new StompFrame(StompCommand.Unknown, null, null);

            StompCommand command;
            List <KeyValuePair <string, string> > headers = new List <KeyValuePair <string, string> >();
            Memory <byte> body = null;

            // attempt to read initial command line
            if (TryReadCommand(ref sequence, out command) == false)
            {
                return(false);
            }

            // read headers
            while (sequence.End == false)
            {
                // read the current header
                if (TryReadHeader(ref sequence, out var header, command != StompCommand.Connect && command != StompCommand.Connected) == false)
                {
                    return(false);
                }

                // null key indicates end of headers
                if (header.Key == null)
                {
                    break;
                }

                headers.Add(header);
            }

            // content length header can help us decide how far to read
            var contenLengthHeader = headers.FirstOrDefault(i => i.Key == "content-length");

            if (contenLengthHeader.Value != null)
            {
                if (int.TryParse(contenLengthHeader.Value, out var contentLength) == false)
                {
                    throw new StompProtocolException("Invalid 'content-length'. Not an integer.");
                }

                // allocate new buffer and copy body contents
                body = new byte[contentLength];
                if (sequence.TryCopyTo(body.Span) == false)
                {
                    return(false);
                }
                sequence.Advance(contentLength);

                // body should be terminated by a null character
                if (sequence.TryRead(out var term) == false)
                {
                    return(false);
                }
                if (term != 0x00)
                {
                    throw new StompProtocolException("Body not terminated by null.");
                }
            }
            else
            {
                if (sequence.TryReadTo(out ReadOnlySpan <byte> buffer, (byte)0x00) == false)
                {
                    return(false);
                }

                // allocate new buffer and copy body contents
                body = new byte[buffer.Length];
                if (buffer.TryCopyTo(body.Span) == false)
                {
                    return(false);
                }
            }

            frame = new StompFrame(command, headers, body);
            return(true);
        }
예제 #5
0
        /// <summary>
        /// Writes the specified frame to the writer.
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="frame"></param>
        public void Write(ArrayBufferWriter <byte> writer, StompFrame frame)
        {
            // write command
            switch (frame.Command)
            {
            case StompCommand.Connect:
                writer.Write(COMMAND_CONNECT);
                break;

            case StompCommand.Stomp:
                writer.Write(COMMAND_STOMP);
                break;

            case StompCommand.Connected:
                writer.Write(COMMAND_CONNECTED);
                break;

            case StompCommand.Send:
                writer.Write(COMMAND_SEND);
                break;

            case StompCommand.Subscribe:
                writer.Write(COMMAND_SUBSCRIBE);
                break;

            case StompCommand.Unsubscribe:
                writer.Write(COMMAND_UNSUBSCRIBE);
                break;

            case StompCommand.Ack:
                writer.Write(COMMAND_ACK);
                break;

            case StompCommand.Nack:
                writer.Write(COMMAND_NACK);
                break;

            case StompCommand.Begin:
                writer.Write(COMMAND_BEGIN);
                break;

            case StompCommand.Disconnect:
                writer.Write(COMMAND_DISCONNECT);
                break;

            case StompCommand.Message:
                writer.Write(COMMAND_MESSAGE);
                break;

            case StompCommand.Receipt:
                writer.Write(COMMAND_RECEIPT);
                break;

            case StompCommand.Error:
                writer.Write(COMMAND_ERROR);
                break;

            default:
                throw new StompProtocolException("Unknown command.");
            }

            // write headers
            foreach (var header in frame.Headers)
            {
                WriteEscapedHeaderValue(writer, header.Key);
                writer.Write(BYTE_COLON);
                WriteEscapedHeaderValue(writer, header.Value);
                writer.Write(BYTE_LF);
            }

            // headers separated from body by a new line
            writer.Write(BYTE_LF);

            // body followed by NULL
            writer.Write(frame.Body.Span);
            writer.Write(BYTE_NULL);
        }
예제 #6
0
        /// <summary>
        /// Handles outgoing messages to the transport.
        /// </summary>
        /// <param name="frame"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        async ValueTask OnSendAsync(StompFrame frame, CancellationToken cancellationToken)
        {
            logger.LogDebug("Sending STOMP frame: {Command}", frame.Command);

            await transport.SendAsync(frame, cancellationToken);
        }