Example #1
0
        /// <summary>
        /// Removes and returns the current trailer value by first checking the HttpResponse
        /// and falling back to the value from HttpResponseMessage only if
        /// <see cref="ResponseTrailersTransformContext.HeadersCopied"/> is not set.
        /// This ordering allows multiple transforms to mutate the same header.
        /// </summary>
        /// <param name="headerName">The name of the header to take.</param>
        /// <returns>The response header value, or StringValues.Empty if none.</returns>
        public static StringValues TakeHeader(ResponseTrailersTransformContext context, string headerName)
        {
            if (context is null)
            {
                throw new System.ArgumentNullException(nameof(context));
            }

            if (string.IsNullOrEmpty(headerName))
            {
                throw new System.ArgumentException($"'{nameof(headerName)}' cannot be null or empty.", nameof(headerName));
            }

            var existingValues          = StringValues.Empty;
            var responseTrailersFeature = context.HttpContext.Features.Get <IHttpResponseTrailersFeature>();
            var responseTrailers        = responseTrailersFeature.Trailers;

            // Support should have already been checked by the caller.
            Debug.Assert(responseTrailers != null);
            Debug.Assert(!responseTrailers.IsReadOnly);

            if (responseTrailers.TryGetValue(headerName, out var responseValues))
            {
                responseTrailers.Remove(headerName);
                existingValues = responseValues;
            }
            else if (!context.HeadersCopied &&
                     context.ProxyResponse.TrailingHeaders.TryGetValues(headerName, out var values))
            {
                existingValues = (string[])values;
            }

            return(existingValues);
        }
Example #2
0
        /// <inheritdoc/>
        public override ValueTask ApplyAsync(ResponseTrailersTransformContext context)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            Debug.Assert(context.ProxyResponse != null);
            Debug.Assert(!context.HeadersCopied);

            // See https://github.com/microsoft/reverse-proxy/blob/51d797986b1fea03500a1ad173d13a1176fb5552/src/ReverseProxy/Forwarder/HttpTransformer.cs#L85-L99
            // NOTE: Deliberately not using `context.Response.SupportsTrailers()`, `context.Response.AppendTrailer(...)`
            // because they lookup `IHttpResponseTrailersFeature` for every call. Here we do it just once instead.
            var responseTrailersFeature = context.HttpContext.Features.Get <IHttpResponseTrailersFeature>();
            var outgoingTrailers        = responseTrailersFeature?.Trailers;

            if (outgoingTrailers != null && !outgoingTrailers.IsReadOnly)
            {
                // Note that trailers, if any, should already have been declared in Proxy's response
                CopyResponseHeaders(context.ProxyResponse.TrailingHeaders, outgoingTrailers);
            }

            context.HeadersCopied = true;

            return(default);
Example #3
0
        /// <summary>
        /// Sets the given trailer on the HttpResponse.
        /// </summary>
        public static void SetHeader(ResponseTrailersTransformContext context, string headerName, StringValues values)
        {
            var responseTrailersFeature = context.HttpContext.Features.Get <IHttpResponseTrailersFeature>();
            var responseTrailers        = responseTrailersFeature.Trailers;

            // Support should have already been checked by the caller.
            Debug.Assert(responseTrailers != null);
            Debug.Assert(!responseTrailers.IsReadOnly);

            responseTrailers[headerName] = values;
        }
        // Assumes the response status code has been set on the HttpContext already.
        /// <inheritdoc/>
        public override ValueTask ApplyAsync(ResponseTrailersTransformContext context)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (Always || Success(context))
            {
                var responseTrailersFeature = context.HttpContext.Features.Get <IHttpResponseTrailersFeature>();
                var responseTrailers        = responseTrailersFeature.Trailers;
                // Support should have already been checked by the caller.
                Debug.Assert(responseTrailers != null);
                Debug.Assert(!responseTrailers.IsReadOnly);

                responseTrailers.Remove(HeaderName);
            }

            return(default);
        // Assumes the response status code has been set on the HttpContext already.
        /// <inheritdoc/>
        public override ValueTask ApplyAsync(ResponseTrailersTransformContext context)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (Always || Success(context))
            {
                var existingHeader = TakeHeader(context, HeaderName);
                if (Append)
                {
                    var value = StringValues.Concat(existingHeader, Value);
                    SetHeader(context, HeaderName, value);
                }
                else
                {
                    SetHeader(context, HeaderName, Value);
                }
            }

            return(default);
Example #6
0
 internal static bool Success(ResponseTrailersTransformContext context)
 {
     // TODO: How complex should this get? Compare with http://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header
     return(context.HttpContext.Response.StatusCode < 400);
 }
Example #7
0
 /// <summary>
 /// Transforms the given response trailers. The trailers will have (optionally) already been
 /// copied to the <see cref="HttpResponse"/> and any changes should be made there.
 /// </summary>
 public abstract ValueTask ApplyAsync(ResponseTrailersTransformContext context);
Example #8
0
 /// <inheritdoc/>
 public override ValueTask ApplyAsync(ResponseTrailersTransformContext context)
 {
     return(_func(context));
 }