-
Notifications
You must be signed in to change notification settings - Fork 4.9k
HttpClient.SendAsync() should not attempt to read response body on a HEAD or PUT request. #38129
Conversation
…HEAD or PUT request.
| return FinishSendAsyncUnbuffered(sendTask, request, cts, disposeCts); | ||
| } | ||
|
|
||
| return completionOption == HttpCompletionOption.ResponseContentRead ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just add the above to this expression, after the completion options check? e.g.
return completionOptions == HttpCompletionOption.ResponseContentRead && request.Method.MayHaveResponseBody ?
FinishSendAsyncBuffered(sendTask, request, cts, disposeCts) :
FinishSendAsyncUnbuffered(sendTask, request, cts, disposeCts);?
HEAD requests are relatively rare; we shouldn't need to pay for the extra dictionary lookup if completionOptions == ResponseHeadersRead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would the comparison still work without that Normalize() thing?
return completionOption == HttpCompletionOption.ResponseContentRead && !HttpMethod.Normalize(request.Method).NoResponseBody ?
FinishSendAsyncBuffered(sendTask, request, cts, disposeCts) :
FinishSendAsyncUnbuffered(sendTask, request, cts, disposeCts);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would the comparison still work without that Normalize() thing?
It depends how it's implemented. Right now the code is calling Normalize (which does a dictionary lookup) and then does a reference comparison... you could instead just do the string comparison.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, cleaned it up and removed the (now unused) property that I added.
src/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs
Outdated
Show resolved
Hide resolved
src/System.Net.Http/tests/FunctionalTests/HttpRequestMessageTest.cs
Outdated
Show resolved
Hide resolved
|
Re this:
This is a bit of a tangent, but I don't understand why LoadIntoBufferAsync doesn't use TryComputeLength to try to determine the content length, rather than reading the Content-Length header directly. If it did this, then we could provide an appropriate implementation of TryComputeLength for HEAD requests that just always returns 0. Right now, we don't actually support TryComputeLength on HttpConnectionResponseContent at all. We just always return false. See: corefx/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionResponseContent.cs Line 51 in 24aa561
While this is perfectly legal, it seems like it would be more useful to actually return the known content length when possible, i.e. because we have a Content-Length header or because it's a HEAD request or a No Content response, etc. I think @davidsh understands this better than I do... |
|
@geoffkizer, your comments and question make sense... I unfortunately don't know the answer. |
…HEAD or PUT request. (dotnet#38129) * HttpClient.SendAsync() should not attempt to read response body on a HEAD or PUT request. * Update HttpMethod.cs * Address feedback. * Cleanup. * Update HttpRequestMessageTest.cs (cherry picked from commit 73e610f)
…HEAD or PUT request. (dotnet#38129) * HttpClient.SendAsync() should not attempt to read response body on a HEAD or PUT request. * Update HttpMethod.cs * Address feedback. * Cleanup. * Update HttpRequestMessageTest.cs (cherry picked from commit 73e610f)
HttpClient.SendAsync() should not attempt to read response body on a HEAD request. * CoreFX PR dotnet/corefx#38129. * Backported in mono/corefx#297. * Fixes mono#14214.
The reading of the Content-Length header implicitly will call TryComputeLength() if the Content-Length header is null. That is part of the original design of how HttpContent uses a lazy-initialized Content-Length calculation which is especially useful for stream-based content (not as useful for memory based like StringContent, etc.). LoadIntoBufferAsync() calls GetComputedOrBufferLength() via accessing the 'HttpContentHeaders.ContentLength' property. corefx/src/System.Net.Http/src/System/Net/Http/Headers/HttpContentHeaders.cs Lines 69 to 106 in c63e5eb
which calls TryComputeLength(): corefx/src/System.Net.Http/src/System/Net/Http/HttpContent.cs Lines 469 to 493 in c63e5eb
HttpConnectionResponseContent currently doesn't implement TryComputeLength(). Conceptually, it can't because it is a response stream which could have arbitrary length despite the promise of a 'Content-Length' header. And chunked responses would result in TryComputeLength() being unable to know ahead of time the real length, of course. You might choose to change HttpConnectionResponseContent.TryComputeLength() to optimize in the case of a 'Content-Length' header being part of the http response headers returned by the server. Not sure if this changes the end result for optimization. |
|
The problem that I have with this is that |
HttpClient.SendAsync() should not attempt to read response body on a HEAD request. * CoreFX PR dotnet/corefx#38129. * Backported in mono/corefx#297. * Fixes #14214.
HttpClient.SendAsync() should not attempt to read response body on a HEAD request. * CoreFX PR dotnet/corefx#38129. * Backported in mono/corefx#297. * Fixes mono#14214.
HttpClient.SendAsync() should not attempt to read response body on a HEAD request. * CoreFX PR dotnet/corefx#38129. * Backported in mono/corefx#297. * Fixes mono#14214. (cherry picked from commit 2b3a23c)
HttpClient.SendAsync() should not attempt to read response body on a HEAD request. * CoreFX PR dotnet/corefx#38129. * Backported in mono/corefx#297. * Fixes #14214. (cherry picked from commit 2b3a23c)
HttpClient.SendAsync() should not attempt to read response body on a HEAD request. * CoreFX PR dotnet/corefx#38129. * Backported in mono/corefx#297. * Fixes #14214.
…HEAD or PUT request. (dotnet/corefx#38129) * HttpClient.SendAsync() should not attempt to read response body on a HEAD or PUT request. * Update HttpMethod.cs * Address feedback. * Cleanup. * Update HttpRequestMessageTest.cs Commit migrated from dotnet/corefx@73e610f
When using
HttpClient.SendAsync()to issue aHEADrequest, we shouldn't attempt to allocate a buffer for the return content regardless of theHttpCompletionOption(becauseResponseContentReadis the default).The problem is that even though the server won't send any response, the
Content-Lengthcould be quite large, so we would unnecessarily allocate a huge buffer. Furthermore, you even get an exception if the length is above 2 GB (HttpContent.MaxBufferSizeisint.MaxValue).We have a regression in Mono about this: mono/mono#14214.
I have investigated this and what's happening is that when using
FinishSendAsyncBuffered(), theresponse.Contentis an instance ofHttpConnection.HttpConnectionResponseContent, soresponse.Content.LoadIntoBufferAsync()is called.The then calls
CreateMemoryStream(maxBufferSize, out error), theHeaders.ContentLengthis (in our example 2167849215) larger thanint.MaxValueand it throws. But even it it were smaller, we'd still allocate a potentially huge buffer for no reason.