Как получить доступ к потоку запросов при использовании HttpClient при создании прокси?C#

Место общения программистов C#
Ответить
Anonymous
 Как получить доступ к потоку запросов при использовании HttpClient при создании прокси?

Сообщение Anonymous »

Я создаю простой прокси-сервер в .NET 8.
Процесс выглядит следующим образом:
  • Клиент отправляет POST-запрос (возможно, большой)
  • Мой прокси-сервер получает этот запрос и должен обработать входящий поток, чтобы фактически изменить его (требуется для нашего приложения)
  • Затем прокси-сервер создает новый HttpRequestMessage с StreamContent
  • Поток входящего запроса считывается частями по 64 КБ (задается в настройках приложения)
  • Каждый фрагмент преобразуется с помощью нашего конкретного процесса.
  • Преобразованные фрагменты затем необходимо записать в StreamContent.
  • После того, как весь входящий запрос будет обработан. обработано, HttpClient публикуется с преобразованной полезной нагрузкой.
Проблема заключается в том, что использование StreamContent требует от меня записи ВСЕХ преобразованных данных в это, и мне нужно установить Position обратно на ноль, прежде чем я смогу отправить его. Это означает (если я правильно понимаю), что весь «новый запрос» находится в памяти на моем прокси-сервере.
Если я использую устаревший HttpWebRequest для своего нового запроса, я могу получить RequestStream и обработать входящее сообщение частями, которые я записываю непосредственно в RequestStream. Кажется, что это гораздо лучший подход, поскольку он должен вызывать меньшую нагрузку на память.
Я что-то упустил?
Ниже приведен код, который я нужно использовать StreamContent:

Код: Выделить всё

/// 
/// Uses the HttpRequestMessage / HttpResponseMessage to communicate with
/// the proxied API.  This requires the entire Request stream to be assembled
/// before it can be sent to the API.
/// 
/// 
The HttpContext for this request.
/// An HttpResponseMessage which exposes its stream for processing.
private async Task SendToProxiedAPIWithStreamContent(HttpContext clientHttpContext)
{
byte[] incomingRequestBuffer = new byte[_settings.ChunkSize];
HttpResponseMessage? response = null;

//
// Create a upstreamRequestStream to write chunks to for sending to the upstream server.
//
using (MemoryStream upstreamRequestStream = new MemoryStream(_settings.ChunkSize))
{
//
// Create a StreamContent with the memory upstreamRequestStream as its internal implementation.
//
StreamContent upstreamContent = new StreamContent(upstreamRequestStream);

//
// Make a Request to send to the proxied API.
//
HttpRequestMessage upstreamRequest = new HttpRequestMessage();
upstreamRequest.Method = new HttpMethod(clientHttpContext.Request.Method);
upstreamRequest.RequestUri = new Uri($"{_settings.UpstreamUrl}/api/postdata");
upstreamRequest.Content = upstreamContent;
upstreamRequest.Content.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse(clientHttpContext.Request.Headers.ContentType.First());

//
// Loop through the incoming upstreamRequestStream sending it to the Transform method
// and then writing it to the upstream data stream.
//
int incomingBufferBytesRead = await clientHttpContext.Request.Body.ReadAsync(incomingRequestBuffer, 0, incomingRequestBuffer.Length);

while (incomingBufferBytesRead > 0)
{
//
// Process (transform) a single chunk prior to its going to the proxied API.
//
byte[] transformedBuffer = TransformTheIncomingBuffer(incomingRequestBuffer, incomingBufferBytesRead);

//
// Write the transformed data to the upstreamRequestStream that is wrapped in the StreamContent.
//
await upstreamRequestStream.WriteAsync(transformedBuffer, 0, transformedBuffer.Length);

//
// Clear my transformed buffer and get the next chunk from the input upstreamRequestStream.
//
Array.Clear(transformedBuffer);
incomingBufferBytesRead = await clientHttpContext.Request.Body.ReadAsync(incomingRequestBuffer, 0, incomingRequestBuffer.Length);
}

//
// Reset the upstreamRequestStream pointer on the outgoing upstreamRequestStream.
// This is a problem - it means the entire object is in memory
// so large objects will overwhelm this.
// How can we feed chunks to the upstream request's StreamContent?
//
upstreamRequestStream.Position = 0;

//
// Send this request on to the httpClient that is bound to the proxied API.
// But, by now we have read and transformed the entire incoming request clientResponseBody
// which may be huge. How do we send this using chunks as we transform it?
//
response = await _httpClient.SendAsync(upstreamRequest);
}

return response;
}
А вот мой код, использующий устаревший HttpWebRequest:

Код: Выделить всё

    /// 
/// Uses the obsolete WebRequest / WebResponse to communicate with
/// the proxied API.   This allows us access to the upstream request stream.
/// 
/// 
The HttpContext for this request.
/// An HttpWebResponse which exposes its stream for processing.
private async Task SendToProxiedAPIWithWebRequest(HttpContext clientHttpContext)
{
byte[] incomingRequestBuffer = new byte[_settings.ChunkSize];
HttpWebResponse? response = null;
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.CreateHttp($"{_settings.UpstreamUrl}/api/postdata");
webRequest.Method = "POST";
webRequest.ContentType = "application/json";

using (var upstreamRequestStream = webRequest.GetRequestStream())
{
int incomingBufferBytesRead = await clientHttpContext.Request.Body.ReadAsync(incomingRequestBuffer, 0, incomingRequestBuffer.Length);
long contentLength = 0;

while (incomingBufferBytesRead > 0)
{
//
// Process (transform) a single chunk prior to its going to the proxied API.
//
byte[] transformedBuffer = TransformTheIncomingBuffer(incomingRequestBuffer, incomingBufferBytesRead);
contentLength += transformedBuffer.LongLength;
//
// Write the transformed data directly to the outgoing request upstreamRequestStream.
// (Note: there is no way to do this using the HttpClient)
//
upstreamRequestStream.Write(transformedBuffer, 0, transformedBuffer.Length);
//
// Clear my transformed buffer and get the next chunk from the input stream.
//
Array.Clear(transformedBuffer);
incomingBufferBytesRead = await clientHttpContext.Request.Body.ReadAsync(incomingRequestBuffer, 0, incomingRequestBuffer.Length);
}

webRequest.ContentLength = contentLength;
}
//
// Send the request to the proxied API and get the httpResponseMessage.
//
response = (HttpWebResponse)await webRequest.GetResponseAsync();
return response;
}
Есть ли способ использовать HttpRequestMessage и при этом обрабатывать данные частями?
Я хотел бы использовать более новый метод поскольку, насколько я понимаю, он гораздо лучше справляется с повторным использованием соединений, а на прокси-сервере большого объема это определенно будет преимуществом.
Заранее спасибо за любые рекомендации.

Подробнее здесь: https://stackoverflow.com/questions/791 ... ding-a-pro
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»