Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,28 @@ public static async Task UseRequestContext()
#endregion
}
}

public static void UpdateRequestContextInInterceptor()
{
#region UpdateRequestContextInInterceptor
// An interceptor that adds an entry to the request context. The appropriate pattern is
// copy-on-write: build a new dictionary and install a new feature, rather than mutating the
// existing Value in place (which would race with the outgoing encoding).
_ = new Pipeline()
.Use(next => new InlineInvoker((request, cancellationToken) =>
{
IRequestContextFeature? feature = request.Features.Get<IRequestContextFeature>();
IDictionary<string, string> current =
feature?.Value ?? new Dictionary<string, string>();
var updated = new Dictionary<string, string>(current)
{
["CorrelationId"] = Guid.NewGuid().ToString()
};
request.Features = request.Features.With<IRequestContextFeature>(
new RequestContextFeature(updated));
return next.InvokeAsync(request, cancellationToken);
}))
.UseRequestContext();
#endregion
}
}
9 changes: 9 additions & 0 deletions src/IceRpc/Features/IRequestContextFeature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ namespace IceRpc.Features;

/// <summary>Represents a feature that holds a <see cref="IDictionary{TKey, TValue}"/> of strings. This feature can be
/// transmitted as a <see cref="RequestFieldKey.Context" /> field with both ice and icerpc.</summary>
/// <remarks>The outgoing request context is encoded lazily, when the request is sent. To avoid racing mutations
/// with encoding, an interceptor that needs to add or remove context entries should build a new dictionary and
/// install a new <see cref="IRequestContextFeature"/>, rather than mutating <see cref="Value"/> in place.
/// </remarks>
/// <example>
/// The following code shows how to update the request context from an interceptor using copy-on-write.
/// <code source="../../../docfx/examples/IceRpc.RequestContext.Examples/RequestContextInterceptorExamples.cs"
/// region="UpdateRequestContextInInterceptor" lang="csharp" />
/// </example>
public interface IRequestContextFeature
{
/// <summary>Gets the value of this feature.</summary>
Expand Down
7 changes: 5 additions & 2 deletions src/IceRpc/Features/RequestContextFeature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ public string this[string key]
/// <summary>Constructs an empty writeable request context feature.</summary>
public RequestContextFeature() => Value = new Dictionary<string, string>();

/// <summary>Constructs a request context feature with the specified dictionary.</summary>
/// <param name="dictionary">The dictionary that the new feature will hold.</param>
/// <summary>Constructs a request context feature wrapping the specified dictionary.</summary>
/// <param name="dictionary">The dictionary that the new feature will hold. The feature stores the reference; it
/// does not copy. The caller must not mutate <paramref name="dictionary"/> after constructing the feature, since
/// the request context is encoded lazily when the request is sent. See
/// <see cref="IRequestContextFeature"/> for the recommended copy-on-write update pattern.</param>
public RequestContextFeature(IDictionary<string, string> dictionary) => Value = dictionary;

/// <summary>Adds a new entry to <see cref="Value" />.</summary>
Expand Down
Loading