Improve testing for StreamableHttpHandler and IdleTrackingBackgroundService (#345)

* Add idle session pruning tests
* Remove redundant CTS from StreamableHttpHandler
  * StreamableHttpServerTransport.HandlePostRequest already creates the equivelant CTS
    which DeleteRequest_CompletesSession_WhichCancelsLongRunningToolCalls verifies
* Fix accept header validation for GET
* Use InvalidOperationException for transport-not-connected errors in doc comments
* Fix flaky Cancellation_ThrowsCancellationException test
This commit is contained in:
Stephen Halter 2025-04-24 10:49:02 -07:00 • committed by GitHub
commit e3ff552824
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 315 additions and 291 deletions

View file

@ -34,7 +34,8 @@ public interface IMcpEndpoint : IAsyncDisposable
/// <param name="request">The JSON-RPC request to send.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task containing the endpoint's response.</returns>
/// <exception cref="McpException">The transport is not connected, or another error occurs during request processing.</exception>
/// <exception cref="InvalidOperationException">The transport is not connected, or another error occurs during request processing.</exception>
/// <exception cref="McpException">An error occured during request processing.</exception>
/// <remarks>
/// This method provides low-level access to send raw JSON-RPC requests. For most use cases,
/// consider using the strongly-typed extension methods that provide a more convenient API.
@ -50,7 +51,7 @@ public interface IMcpEndpoint : IAsyncDisposable
/// </param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that represents the asynchronous send operation.</returns>
/// <exception cref="McpException">The transport is not connected.</exception>
/// <exception cref="InvalidOperationException">The transport is not connected.</exception>
/// <exception cref="ArgumentNullException"><paramref name="message"/> is <see langword="null"/>.</exception>
/// <remarks>
/// <para>

View file

@ -51,7 +51,7 @@ internal sealed class SseWriter(string? messageEndpoint = null, BoundedChannelOp
{
Throw.IfNull(message);
using var _ = await _disposeLock.LockAsync().ConfigureAwait(false);
using var _ = await _disposeLock.LockAsync(cancellationToken).ConfigureAwait(false);
if (_disposed)
{

View file

@ -51,8 +51,8 @@ public sealed class StreamableHttpServerTransport : ITransport
throw new InvalidOperationException("Session resumption is not yet supported. Please start a new session.");
}
using var getCts = CancellationTokenSource.CreateLinkedTokenSource(_disposeCts.Token, cancellationToken);
await _sseWriter.WriteAllAsync(sseResponseStream, getCts.Token).ConfigureAwait(false);
// We do not need to reference _disposeCts like in HandlePostRequest, because the session ending completes the _sseWriter gracefully.
await _sseWriter.WriteAllAsync(sseResponseStream, cancellationToken).ConfigureAwait(false);
}
/// <summary>