Should I error-check Close() on a response body? - go

The docs for net/http have the following example:
resp, err := http.Get("http://example.com/")
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Printf("%s", body)
Close returns an error, but it is not checked. Is there something I'm missing here? The importance of checking every error is frequently emphasized in go, but I see this defer resp.Body.Close() pattern a lot with no error checks.

There are two things to consider: What would you do with it if you checked it and there was an error? And, what would the side-effects be if there was an error?
In most cases, for closing a response body, the answer to both questions is... absolutely nothing. If there's nothing you'd do if there was an error and the error has no appreciable impact, there's no reason to check it.
Also note that Close() returns an error in order to fulfill the io.Closer interface; the function doesn't necessarily return an error. You'd need to check the source to know for sure if it has an error case.

This is a downside of using defer
It would be advised, as a responsible developer, that you check for all possible error prone points, and handle them as gracefully as you can.
Here are some of the options you can choose in handling this situation:
Option #1
Do not use defer, instead manually call close once you're done with the response's body and simply check for errors then.
Option #2
Create an anonymous function that wraps the closing and error checking code. Something like this:
defer func() {
err := resp.Body.Close()
if err != nil {
log.Fatal(err)
}
}()
Avoid using panics in your programs. Try to handle the errors gracefully by doing something or at least logging the error.
Additional information can be found here.

To add to #Mihailo option #2, call it option #2.1
Define function dclose() like so:
func dclose(c io.Closer) {
if err := c.Close(); err != nil {
log.Fatal(err)
}
}
use like so:
defer dclose(resp.Body)
Also in your code the check for err!=nil can declare:
func errcheck(err error) {
if err != nil {
log.Fatal(err)
}
}
then use:
errcheck(err)
then your code becomes:
resp, err := http.Get("http://example.com/")
errcheck(err)
defer dclose(resp.Body)
body, err := ioutil.ReadAll(resp.Body)
errcheck(err)
fmt.Printf("%s", string(body))
IMHO a little cleaner perhaps? But I'll wait for the go aficionado's to correct me and highlight drawbacks.
EDIT
Thanks! #RayfenWindspear
Do replace log.Fatal(err) with log.Println(err) to avoid unnecessary panic.
EDIT2
dclose() to avoid confusion with go close()
Have fun!

From what I could gather, net/http can't err. However, I would rather assume that .Close() can err for any implementation of io.Closer than to study the internals.
Below is an example using named returns where defer only sets the returned error if the error would otherwise be nil:
func printResponse(url string) (retErr error) {
resp, err := http.Get(url)
if err != nil {
return err
}
defer func() {
err := resp.Body.Close()
if err != nil && retErr == nil {
retErr = err
}
}()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
fmt.Printf("%s", body)
return nil
}

Related

What do empty returns in the main function mean?

I copy-pasted the code from an API (https://api.magiceden.dev/). This code gets the link and prints a slice. Here's the code:
func main() {
url := "https://api-mainnet.magiceden.dev/v2/wallets/6xX3z7uxTNB68izZW2GHKnzno49dizqeVVc5ncVzdjFM/activities?offset=0&limit=100"
method := "GET"
client := &http.Client{}
req, err := http.NewRequest(method, url, nil)
if err != nil {
fmt.Println(err)
return
}
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
I'm new to Go, and I know about empty return statements in other functions, but what is returned in main function? That's the question and I still haven't found the answer.
I tried googling it, but I couldn't find any info or examples of empty return statements in main function.
When there is no return type in the function signature the return in such a function just stops the processing of the function at this point. No further statement are run then, but the registered defer functions are processed in the reverse order they have been registered.

How to call Body.Close() when using require()?

API test has below boiler plate code.
I want to call resp.Body.Close() even when require.Equal(ru.ts.T(), tp.expectedStatus, resp.StatusCode) fails.
Currently i have script like below. Are there better ways to write this?
I want to avoid condition if tp.expectedStatus != resp.StatusCode and call resp.Body.Close() when require.Equal(ru.ts.T(), tp.expectedStatus, resp.StatusCode) fails.
func Invoke(ip RestParams, es int) *http.Response {
client := &http.Client{}
// Initialize req using ip
resp, err := client.Do(req)
...
if tp.expectedStatus != resp.StatusCode {
resp.Body.Close()
require.Equal(ru.ts.T(), tp.expectedStatus, resp.StatusCode)
return nil
}
return resp
}
You should almost always do
resp, err := client.Do(req)
if err != nil {...} // Or require.NoError(err)
defer resp.Body.Close()
http package garanties a non nil body that should be closed as soon as the error is nil.
As a side note, I think you should avoid returning the http.Response. Unmarshal it here and return a struct model so you can handle all your technical http layer in this functio.

Why defer a Rollback?

I have started using Go for a web-service and have some database interaction (surprise!!!) and I have found this example:
tx, err := db.Begin()
if err != nil {
log.Fatal(err)
}
defer tx.Rollback()
stmt, err := tx.Prepare("INSERT INTO foo VALUES (?)")
if err != nil {
log.Fatal(err)
}
defer stmt.Close() // danger!
for i := 0; i < 10; i++ {
_, err = stmt.Exec(i)
if err != nil {
log.Fatal(err)
}
}
err = tx.Commit()
if err != nil {
log.Fatal(err)
}
// stmt.Close() runs here!
From http://go-database-sql.org/prepared.html
The example is well formulated an easy to understand. However, it leaves me with an unanswered question. Why defer the transaction Rollback call?
Why not just do the following:
err := tx.Commit()
if err != nil {
log.Error(err)
tx.Rollback()
}
Would defer tx.Rollback() not always attempt a rollback? Even if tx.Commit() was a success, or have I misunderstood something about defer?
The important thing is that if you defer tx.Rollback() you are sure that it will be called even if you do an early return and the "trick" is that calling tx.Rollback() on a committed transaction will not actually do the rollback, because once a transaction is committed, it's committed and there is no way to roll it back :)
So this is a neat trick on how to keep the code simple.
The example is a little bit misleading. It uses log.Fatal(err) for error handling. You wouldn't normally do that, and instead return err. So the deferred rollback is there to ensure that the transaction is rolled back in case of an early return.

Properly handling errors

Typically in Go you find the following convention:
res, err := thingThatCanError(arg)
if err != nil {
// handle it
}
However, it's obvious this gets VERY unruly very quickly for a large number of these calls:
res, err := thingThatCanError(arg)
if err != nil {
// handle it
}
res, err2 := thingThatCanError(arg)
if err2 != nil {
// handle it
}
res, err3 := thingThatCanError(arg)
if err3 != nil {
// handle it
}
There's more lines of boilerplate error handling than code! This website says to avoid this but does not give an example on how to clean up this smell. A useful example comes straight from the Go blog that shows us how to clean up a homogenous HTTP app with an error handler that makes sense.
But imagine each of these calls aren't homogenous, as in with the same "central idea", so a single "error handler struct" wouldn't make a lot of sense.
Is there a way to clean up this type of code smell with functions that don't "mesh together" nicely in terms of errors?
Unfortunately there's sometimes no way around these patterns. You could use panic/defer as a makeshift try/catch system but the community looks down upon it.
If statements in Go can be combined with assignments so
err := thing.Do()
if err != nil {
return err
}
can become
if err := thing.Do(); err != nil {
return err
}

Interpreting Golang Error codes

So most examples of go error handling I see just pass any errors back up the stack. At some point these need interpreting and this is what I am trying to do.
Here's a snippet of my attempt:
resp, err := http.Get(string(url))
defer out_count.Dec()
if err != nil {
switch err {
case http.ErrBodyReadAfterClose:
fmt.Println("Read after close error")
case http.ErrMissingFile:
fmt.Println("Missing File")
{some more cases here}
case io.EOF:
fmt.Println("EOF error found")
default:
fmt.Printf("Error type is %T\n", err)
panic(err)
}
return
This isn't working though for my current case(edited to remove url}:
ERROR: Failed to crawl "http://{removed URL}"
Error type is *url.Error
panic: Get http://{removed url}: EOF
goroutine 658 [running]:
runtime.panic(0x201868, 0x106352c0)
/usr/lib/go/src/pkg/runtime/panic.c:279 +0x1a0
github.com/cbehopkins/grab/grab.crawl(0x10606210, 0x27, 0x105184b0, 0x105184e0, 0x10500460)
I can't figure out a way to get the switch statement to catch this error since the text of the error changes every time and has no explicit value I can catch against. (as the URL changes all the time). Now maybe I could do some sort of regex match in the case statement or sub-slice the error string, but that feels like a very bad way to solve this problem.
Any suggestions? There must be an idiomatic way to catch errors such as this surely?
The simplest way would be to have package level error values in your code:
var URLFetchError = errors.New("Cannot fetch URL")
url := "http://www.google.com"
res, err := http.Get(url)
if err != nil {
return URLFetchError
}
The switch then becomes:
switch err {
case http.ErrBodyReadAfterClose:
fmt.Println("Read after close error")
case URLFetchError:
fmt.Println("Error fetching URL")
If you want to pass more information with the error, you can create your own custom errors:
type MyError struct {
URL string
}
func (e MyError) Error() string {
return fmt.Sprintf("Error getting: %v", e.URL)
}
Then, you can create this error whenever required. For example:
url := "http://www.google.com"
res, err := http.Get(url)
if err != nil {
return MyError{url}
}
Finally, in your error checking method, you can use type switches instead of simple switches to get the error:
switch err.(type) {
case MyError:
fmt.Println("Error:", err)
default:
fmt.Println("No Error")
}
In your case, since you have a mix of regular error, you can include this check in a nested switch:
switch err {
case http.ErrBodyReadAfterClose:
fmt.Println("Read after close error")
case http.ErrMissingFile:
fmt.Println("Missing File")
case io.EOF:
fmt.Println("EOF error found")
default: // check for custom errors
switch err.(type) {
case MyError:
fmt.Println("custom error:", err)
default:
panic(err)
}
}
You can implement the error interface and create your own errors which you may find easier to deal with.
For runtime errors a.k.a panic. You can recover by including recover() in a function that you think may panic. It get's called before a panicing function returns.
defer func() {
if r := recover(); r != nil {
if _, ok := r.(runtime.Error); ok {
err = r.(error) //panic(r)
}
err = r.(error)
}
}()
}

Resources