Okay, so I have this piece of code
func registerDomain(domainName string, n int) bool {
//building the request here
resp, errr := client.Do(r)
if errr != nil {
if n == 1 {
return false
}
registerDomain(domainName, n-1)
}
bodyBytes, err2 := ioutil.ReadAll(resp.Body)
if err2 == nil {
resp.Body.Close()
//handle bodyBytes
//if the response is how it should be return true, if it's not call the function again with n-1
} else { //if there is an error reading the response
resp.Body.Close()
if n == 1 {
return false
}
registerDomain(domainName, n-1)
}
return false //it should never reach this line
}
Explanation:
I call the function with an argument n (let's say 5) that represents the number of times the function will retry if something is not right. Every time something is wrong I make a recursive call with n-1 so when it reaches n=1 it gives up and returns false. This code works fine in practice, it does what it's supposed to do, sometimes when the response is not correct it calls itself recursively and it works the second (or 3rd, 4th..) time around. When the problem isn't fixed before n=1 it returns false.
The problem:
This is a part of a big code that is supposed to run for about 17 hours and it panics when it tries to read from the body sometimes on this line:
bodyBytes, err2 := ioutil.ReadAll(resp.Body)
it says panic: runtime error: invalid memory address or nil pointer dereference. Now I know that probably means that it's trying to read from a nonexistent resp.Body but the Go documentation clearly states that When err is nil, resp always contains a non-nil resp.Body.
So, in my head it's probably something with the recursive calls. The only thing that makes sense to me is this scenario: lets say that the errr is not nil (this means that the resp.Body doesn't exist), so it goes inside of the if errr != nil and because n!=1, it will call itself again with n=4. Let's say this time everything is as it should be and the second function returns true to the first one BUT the first one continues with execution and tries to read from the resp.Body which doesn't exist. That causes panic and here we are...
So, what I need is someone that knows exactly how recursive functions work and if it's not that, can I somehow check the existence of resp.Body before I read form it, or something that will help.
Thanks anyways! :)
UPDATE: You were all right, my code doesn't panic anymore and neither do I. Thank you very much! (I'm not sure if this is where the update goes)
Inside the registerDomain()
function there are 2 places where you call itself.
If the client.Do()
failes (errr != nil
), you will call registerDomain()
again. Sometime it will return, and when it does, your code execution will continue, trying to read from resp.Body
but that most likely is nil
because errr != nil
.
Here is how you should handle it:
Whenever you make a recursive call, you should not let code continue when that call returns, but return the value it returned, something like this:
return registerDomain(domainName, n-1)
The problem you're trying to solve can be solved without recursion using a for
statement. The for
variant will even be clearer and more simple.
Modify your registerDomain()
function to return false at each point it calls itself, and use this loop to retry 5 times:
var result bool
for i := 0; i < 5; i++ {
result = registerDomain(domainName) // You don't need to pass n anymore
if result {
break
}
}
if result {
fmt.Println("Domain registered successfully!")
} else {
fmt.Println("Failed to register domain!")
}