private async Task ProcessRequestsAsync(PipeReader reader) { while (true) { ReadResult readResult = default; bool endConnection = false; _currentCommand = StompCommand.None; _currentHeaders.Clear(); do { readResult = await reader.ReadAsync(); }while (!TryParseRequest(readResult, out endConnection)); if (endConnection) { return; } DoResponse(); if (readResult.IsCompleted) { break; } } reader.Complete(); }
/// <summary> /// Sends the specified frame, and registers a completion for the response frame by receipt ID. /// </summary> /// <param name="command"></param> /// <param name="headers"></param> /// <param name="body"></param> /// <param name="cancellationToken"></param> /// <returns></returns> ValueTask <StompFrame> SendFrameAndWaitWithReceiptAsync(StompCommand command, IEnumerable <KeyValuePair <string, string> > headers, ReadOnlyMemory <byte> body, CancellationToken cancellationToken) { headers ??= Enumerable.Empty <KeyValuePair <string, string> >(); var receiptIdText = Interlocked.Increment(ref prevReceiptId).ToString(); headers = headers.Prepend(new KeyValuePair <string, string>("receipt", receiptIdText)); cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, new CancellationTokenSource(options.ReceiptTimeout).Token).Token; return(SendFrameAndWaitAsync(command, headers, body, f => f.GetHeaderValue("receipt-id") == receiptIdText, cancellationToken)); }
/// <summary> /// Sends the specified frame, and registers a completion for the next frame that matches the specified conditions. /// </summary> /// <param name="command"></param> /// <param name="headers"></param> /// <param name="body"></param> /// <param name="response"></param> /// <param name="cancellationToken"></param> /// <returns></returns> async ValueTask <StompFrame> SendFrameAndWaitAsync(StompCommand command, IEnumerable <KeyValuePair <string, string> > headers, ReadOnlyMemory <byte> body, Func <StompFrame, bool> response, CancellationToken cancellationToken) { headers ??= Enumerable.Empty <KeyValuePair <string, string> >(); // schedule a router for the frame filter var tcs = new TaskCompletionSource <StompFrame>(); ValueTask <bool> WriteAsync(StompFrame frame, CancellationToken cancellationToken) { tcs.SetResult(frame); return(new ValueTask <bool>(false)); } ValueTask AbortAsync(Exception exception, CancellationToken cancellationToken) { tcs.SetException(exception); return(ValueTaskHelper.CompletedTask); } var hnd = new FrameRouter(response, WriteAsync, AbortAsync); // handle cancellation through new task cancellationToken.Register(() => tcs.TrySetCanceled(cancellationToken)); // subscribe to matching frames var node = await RegisterRouterAsync(hnd, cancellationToken); try { // send initial frame and wait for resumption await SendFrameAsync(new StompFrame(command, headers, body), cancellationToken); return(await tcs.Task); } finally { // ensure listener is removed upon completion if (node.List != null) { using (await routersLock.LockAsync()) if (node.List != null) { routers.Remove(node); } } } }
/// <summary> /// Attempts to read the next line from the sequence. /// </summary> /// <param name="sequence"></param> /// <param name="text"></param> /// <returns></returns> bool TryReadCommand(ref SequenceReader <byte> sequence, out StompCommand command) { command = StompCommand.Unknown; // read up to line ending if (sequence.TryReadTo(out ReadOnlySequence <byte> itemBytes, (byte)'\n') == false) { return(false); } // if so, it should be the command name if (TryParseCommand(Encoding.UTF8.GetString(itemBytes).TrimEnd(), out command) == false) { throw new StompProtocolException("Cannot parse STOMP command."); } return(true); }
public AppFunc Invoke(AppFunc next) => async environment => { var requestFrame = environment.ReadFromEnvironmentRequest(); if (StompCommand.IsConnectRequest(requestFrame.Command)) { string stompAcceptVersion; var stompProtocolVersion = requestFrame.Headers.TryGetValue("accept-version", out stompAcceptVersion) ? stompAcceptVersion : "1.0"; if (environment.ContainsKey("stomp.protocolVersion")) { environment["stomp.protocolVersion"] = stompProtocolVersion; } else { environment.Add("stomp.protocolVersion", stompProtocolVersion); } } await next(environment); };
public void OnCommandLine(StompCommand command) { _connection.OnCommandLine(command); }
public void OnCommandLine(StompCommand command) { _currentCommand = command; }
public AppFunc Invoke(AppFunc next) => async environment => { var stompCommand = (string)environment["stomp.requestMethod"]; if (StompCommand.IsConnectRequest(stompCommand)) { string version; object versionObject; if (!environment.TryGetValue("stomp.protocolVersion", out versionObject)) { version = "1.0"; } else { version = versionObject as string; } if (Options.AcceptedVersions.Contains(version)) { var sessionId = Guid.NewGuid().ToString(); new StompFrame(StompCommand.CONNECTED, new Dictionary <string, string> { ["version"] = version, ["session"] = sessionId }).WriteToEnvironmentResponse(environment); //Give other middleware a chance to disagree. await next(environment); //If there wasn't a disagreement. if ((string)environment["stomp.responseMethod"] == StompCommand.CONNECTED) { Options.AddSession(sessionId); } } else { new StompFrame(StompCommand.ERROR, new Dictionary <string, string> { { "message", "No acceptable protocol version negotiated." } }).WriteToEnvironmentResponse(environment); } } else { var headers = (ImmutableArray <KeyValuePair <string, string> >)environment["stomp.requestHeaders"]; string sessionId; if (headers.TryGetValue("session", out sessionId) && !string.IsNullOrWhiteSpace(sessionId)) { if (Options.ValidateSession(sessionId)) { await next(environment); } else { new StompFrame(StompCommand.ERROR, new Dictionary <string, string> { { "message", "Invalid or expired/rejected session identifier." } }).WriteToEnvironmentResponse(environment); } } else { new StompFrame(StompCommand.ERROR, new Dictionary <string, string> { { "message", "Session not established." } }).WriteToEnvironmentResponse(environment); } } };
/// <summary> /// Initializes a new instance. /// </summary> /// <param name="command"></param> /// <param name="headers"></param> /// <param name="body"></param> public StompFrame(StompCommand command, IEnumerable <KeyValuePair <string, string> > headers, ReadOnlyMemory <byte> body) { Command = command; Headers = headers ?? Enumerable.Empty <KeyValuePair <string, string> >(); Body = body; }
/// <summary> /// Parses the command line string. /// </summary> /// <param name="line"></param> /// <param name="command"></param> /// <returns></returns> bool TryParseCommand(string line, out StompCommand command) { switch (line) { case "CONNECT": command = StompCommand.Connect; return(true); case "STOMP": command = StompCommand.Stomp; return(true); case "CONNECTED": command = StompCommand.Connected; return(true); case "SEND": command = StompCommand.Send; return(true); case "SUBSCRIBE": command = StompCommand.Subscribe; return(true); case "UNSUBSCRIBE": command = StompCommand.Unsubscribe; return(true); case "ACK": command = StompCommand.Ack; return(true); case "NACK": command = StompCommand.Nack; return(true); case "BEGIN": command = StompCommand.Begin; return(true); case "DISCONNECT": command = StompCommand.Disconnect; return(true); case "MESSAGE": command = StompCommand.Message; return(true); case "RECEIPT": command = StompCommand.Receipt; return(true); case "ERROR": command = StompCommand.Error; return(true); default: command = StompCommand.Unknown; return(false); } ; }