Please see the example here https://pkg.go.dev/net/http#example-Get. The snipped below as well:
func main() {
res, err := http.Get("http://www.google.com/robots.txt")
if err != nil {
log.Fatal(err)
}
body, err := io.ReadAll(res.Body)
res.Body.Close() // Why!?
if res.StatusCode > 299 {
log.Fatalf("Response failed with status code: %d and\nbody: %s\n", res.StatusCode, body)
}
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s", body)
}
My question is: Why do I need to close the res.Body.Close()
in line no 7. I did not open it, I read from it, but I didn't open it. If function ReadAll opens it, should it close it? Why do I have to remember this extra step?
CodePudding user response:
From the http.Client docs:
... If the Body is not both read to EOF and closed, the Client's underlying RoundTripper (typically Transport) may not be able to re-use a persistent TCP connection to the server for a subsequent "keep-alive" request.
In short, if you don't follow this advice, you will accumulate "dead" http connections in your program, as requests are executed - but never cleaned up and/or reused. Your program's performance will degrade overtime - and depending on how many connections are generated - you make exhaust finite resources like file-descriptors that handle network connections.
CodePudding user response:
Have a look at Get func in http package,
func Get(url string) (resp *Response, err error) {
return DefaultClient.Get(url)
}
it returns a pointer to Response which is a struct containing Body
type Response struct {
.
.
.
Body io.ReadCloser
.
.
}
dig through the io package source code, you will find out that ReadCloser involves io.Reader and io.Writer. See this
Closing an open Reader would terminate a waiting channel and the underlying goroutine would be closed. Leaving too many of these open channels would block the host os to allocate free processes to the running program which is normally limited to consume a certain amount of processes (it could be altered to devour whole).