Процесс выглядит следующим образом:
- Клиент отправляет POST-запрос (возможно, большой)
- Мой прокси-сервер получает этот запрос и должен обработать входящий поток, чтобы фактически изменить его (требуется для нашего приложения)
- Затем прокси-сервер создает новый HttpRequestMessage с StreamContent
- Поток входящего запроса считывается частями по 64 КБ (задается в настройках приложения)
- Каждый фрагмент преобразуется с помощью нашего конкретного процесса.
- Преобразованные фрагменты затем необходимо записать в StreamContent.
- После того, как весь входящий запрос будет обработан. обработано, HttpClient публикуется с преобразованной полезной нагрузкой.
Если я использую устаревший 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;
}
Код: Выделить всё
///
/// 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;
}
Я хотел бы использовать более новый метод поскольку, насколько я понимаю, он гораздо лучше справляется с повторным использованием соединений, а на прокси-сервере большого объема это определенно будет преимуществом.
Заранее спасибо за любые рекомендации.
Подробнее здесь: https://stackoverflow.com/questions/791 ... ding-a-pro
Мобильная версия