Reputation: 2903
I've been struggling for a while with the HttpClient
in .NET 4.5. With a large streamed upload to a WebApi endpoint, via a chunked transfer, it fails to stop when the server has already responded mid-request with a non-success status code (not found, authentication, authorization, validation errors, etc).
Looking with the debugger inside the ConnectStream
class, it knows that it received a non-successful status from the server. The HTTP status and data received from the server are being stored for later, and from that moment on it just pretends to read from the stream until the end. Nothing is being written on the wire from that moment on, and the response is later offered via the HttpResponseMessage
. This unexplained behavior is causing massive problems for me as the stream in question can be quite large. Useless time is spent reading the stream till the end, plus incorrect upload reports are being displayed to the user. I stopped the web server, the remote machine, even disabled the local network interface. The HttpClient
doesn't care. It keeps on reading from the provided stream.
I tried with HttpCompletionOption.ResponseHeadersRead
and StreamedContent
or PushStreamContent
. Same behavior. I've also tried with the HttpWebRequest
, however that doesn't allow writing on the output stream and reading the incoming stream at the same time. Is there any way to stop the client from sending or pretending to send a stream after it received a response from the server? Why is the HttpClient
behaving like this?
var httpClientHandler = new WebRequestHandler
{
AllowAutoRedirect = true,
PreAuthenticate = false,
CookieContainer = cookieContainer,
UseCookies = true,
CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore),
AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip
};
var httpClient = new HttpClient(_httpClientHandler)
{
BaseAddress = baseApiUrl,
Timeout = Timeout.InfiniteTimeSpan;
};
httpClient.DefaultRequestHeaders.TransferEncodingChunked = true;
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain", 0.8));
httpClient.DefaultRequestHeaders.AcceptCharset.Add(new StringWithQualityHeaderValue("UTF-8"));
httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(appName, appVersion));
var requestContent = new StringContent(serializedRequest, Encoding.UTF8, "application/json");
// the monitored stream is an implementation of the stream that proxies the calls to a classic file stream
// here I monitor the calls made to Read(byte[] buffer, int offset, int count) method
// tried with both CanSeek = false or true - the HttpClient reads the whole stream no matter what.
var streamedContent = new StreamContent(monitoredStream);
streamedContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
FileName = "upload.bin",
Size = monitoredStream.Length
};
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, relativeUrl)
{
Content = new MultipartFormDataContent
{
requestContent,
streamedContent
}
};
// the Owin middleware could fail checking authentication, an authorization error could occur,
// or the WebApi controller could receive the first part of the multipart data and reject the request
// from the moment data was received from the server, mid-stream, I could safely unplug the network cable
// socket erros are being ignored, actually the underlying socket is no longer used
// the response message contains the status code and data received during the transmission of the request at the end of this call
var httpResponseMessage = await _httpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
Upvotes: 5
Views: 1011
Reputation: 2903
It's a confirmed limitation inside the HttpClient
. Here is the Microsoft Connect case. Maybe it'll get implemented in one of the next releases, but it wouldn't hurt voting for it.
Upvotes: 2