Am I missing something or is none of this information documented in net/http itself? This is a surprising implementation of "Close" and should be documented somewhere.
I can attest to the fact this is a Frequently Asked Question among people who are trying to Go as an HTTP client, so even though this feels somewhat icky, I can understand where it is coming from. However, I'm also missing, if I want to slam this connection closed, which is a reasonable thing to want, how do I do that without uselessly draining a socket I don't intend to reuse? Fake the "Connection: close" header into the Response object? Hope I can dig out an underlying socket of some sort under the concrete type of the Body.Response that I can bypass this code with? Seems like there should be a way to do that too.
https://pkg.go.dev/net/http#Client.Do
> Note, however, that Transport will automatically try to read a Response Body to EOF asynchronously up to a conservative limit when a Body is closed.
Similar wording appears in a few other places; search for “asynchronously”.
I think it is reasonable for it to not be documented directly on Close. Close is arguably too high level for this: for example, on HTTP/2 this doesn't do what you would intend at all, since the request lifecycle is completely untethered from any underlying socket. If you really wanted to slam the connection closed ASAP it does actually seem more logical to grab the underlying connection and directly force it closed, even though this feels uglier since it requires knowing the underlying layers. To me this "Close" is logically from the perspective of the HTTP request itself and if I'm not getting myself confused and misunderstanding it doesn't really close the connection anyways (e.g. if there is no body and the connection is keep-alive'd, this wouldn't close the underlying connection, right?)
A CloseNow() error method would make sense.
HTTP libraries in general are tough because HTTP in practice requires understanding of at least DNS and TLS and in a lot of cases L4 networking as well. If you try to target application developers you end up with leaky abstractions (unread H/1 body draining behavior when you don't even know what HTTP version you're using) or a library low level enough that application developers don't know how to use it. See rust's hyper and reqwest as an example of an attempt at that split.