/// <summary> /// A callback that is invoked prior to sending the proxied request. All HttpRequestMessage fields are /// initialized except RequestUri, which will be initialized after the callback if no value is provided. /// See <see cref="RequestUtilities.MakeDestinationAddress(string, PathString, QueryString)"/> for constructing a custom request Uri. /// The string parameter represents the destination URI prefix that should be used when constructing the RequestUri. /// The headers are copied by the base implementation, excluding some protocol headers like HTTP/2 pseudo headers (":authority"). /// </summary> /// <param name="httpContext">The incoming request.</param> /// <param name="proxyRequest">The outgoing proxy request.</param> /// <param name="destinationPrefix">The uri prefix for the selected destination server which can be used to create the RequestUri.</param> public virtual ValueTask TransformRequestAsync(HttpContext httpContext, HttpRequestMessage proxyRequest, string destinationPrefix) { foreach (var header in httpContext.Request.Headers) { var headerName = header.Key; var headerValue = header.Value; if (RequestUtilities.ShouldSkipRequestHeader(headerName)) { continue; } RequestUtilities.AddHeader(proxyRequest, headerName, headerValue); } // https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.3 // If a message is received with both a Transfer-Encoding and a // Content-Length header field, the Transfer-Encoding overrides the // Content-Length. Such a message might indicate an attempt to // perform request smuggling (Section 9.5) or response splitting // (Section 9.4) and ought to be handled as an error. A sender MUST // remove the received Content-Length field prior to forwarding such // a message downstream. if (httpContext.Request.Headers.ContainsKey(HeaderNames.TransferEncoding) && httpContext.Request.Headers.ContainsKey(HeaderNames.ContentLength)) { proxyRequest.Content?.Headers.Remove(HeaderNames.ContentLength); } // https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.2 // The only exception to this is the TE header field, which MAY be // present in an HTTP/2 request; when it is, it MUST NOT contain any // value other than "trailers". if (ProtocolHelper.IsHttp2OrGreater(httpContext.Request.Protocol)) { var te = httpContext.Request.Headers.GetCommaSeparatedValues(HeaderNames.TE); if (te != null) { for (var i = 0; i < te.Length; i++) { if (string.Equals(te[i], "trailers", StringComparison.OrdinalIgnoreCase)) { var added = proxyRequest.Headers.TryAddWithoutValidation(HeaderNames.TE, te[i]); Debug.Assert(added); break; } } } } return(default);
/// <summary> /// A callback that is invoked prior to sending the proxied request. All HttpRequestMessage fields are /// initialized except RequestUri, which will be initialized after the callback if no value is provided. /// See <see cref="RequestUtilities.MakeDestinationAddress(string, PathString, QueryString)"/> for constructing a custom request Uri. /// The string parameter represents the destination URI prefix that should be used when constructing the RequestUri. /// The headers are copied by the base implementation, excluding some protocol headers like HTTP/2 pseudo headers (":authority"). /// </summary> /// <param name="httpContext">The incoming request.</param> /// <param name="proxyRequest">The outgoing proxy request.</param> /// <param name="destinationPrefix">The uri prefix for the selected destination server which can be used to create the RequestUri.</param> public virtual ValueTask TransformRequestAsync(HttpContext httpContext, HttpRequestMessage proxyRequest, string destinationPrefix) { foreach (var header in httpContext.Request.Headers) { var headerName = header.Key; var headerValue = header.Value; if (StringValues.IsNullOrEmpty(headerValue) || RequestUtilities.ShouldSkipRequestHeader(headerName)) { continue; } RequestUtilities.AddHeader(proxyRequest, headerName, headerValue); } return(default);