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; }
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); } } }