I had the same issue as https://github.com/golang/go/issues/26666 because I have a wrap function for my http requests.
Sometimes I need to request:
body := new(bytes.Buffer)
json.NewEncoder(body).Encode(h)
req("POST", "http://example.com", body)
And sometimes it's simply:
req("GET", "http://example.com", nil)
runtime error: invalid memory address or nil pointer dereference
I ended up with:
req("GET", "http://example.com", new(bytes.Buffer))
But I'm not sure if it's the right thing to do.
The function:
func req(method string, url string, body *bytes.Buffer) int {
req, err := http.NewRequest(method, url, body)
req.Header.Set("Content-Type", "application/json")
req.SetBasicAuth(user, psw)
resp, err := client.Do(req)
checkErr(err)
if resp.StatusCode > 500 {
time.Sleep(30 * time.Second)
resp, err = client.Do(req)
checkErr(err)
}
defer resp.Body.Close()
return resp.StatusCode
}
Updated function:
func req(method string, url string, body io.Reader) int {
req, err := http.NewRequest(method, url, body)
req.Header.Set("Content-Type", "application/json")
req.SetBasicAuth(user, psw)
resp, err := client.Do(req)
checkErr(err)
defer resp.Body.Close()
if resp.StatusCode >= 500 {
time.Sleep(30 * time.Second)
req, err := http.NewRequest(method, url, body)
req.Header.Set("Content-Type", "application/json")
req.SetBasicAuth(user, psw)
resp, err := client.Do(req)
checkErr(err)
defer resp.Body.Close()
}
return resp.StatusCode
}
func checkErr(err error) {
if err != nil {
log.Fatal(err)
}
}
The body
in http.NewRequest()
is optional, so passing nil
is acceptable when you're doing GET
requests.
The problem is that the body
parameter of http.NewRequest
is an interface type: io.Reader
, and you're attempting to pass a value of a concrete type *bytes.Buffer
. What happens is that this nil
pointer will be wrapped in a non-nil
interface value, and that will be passed to http.NewRequest
as the body.
If you don't have a body, pass nil
explicitly, like this:
func req(method string, url string, body *bytes.Buffer) int {
var bodyToPass io.Reader
if body != nil {
bodyToPass = body
}
req, err := http.NewRequest(method, url, bodyToPass)
// ....
}
And then you can call it like:
req("GET", "http://example.com", nil)
Although best would be if your req()
function would take io.Reader
in the first place, so you don't have to check its value explicitly:
func req(method string, url string, body io.Reader) int {
req, err := http.NewRequest(method, url, body) // You may pass it as-is
// ....
}
And you can call it with nil
or with a non-nil
*bytes.Buffer
too:
req("GET", "http://example.com", nil) // OK
req("POST", "http://example.com", bytes.NewBufferString("data")) // Also OK
For more details, see Hiding nil values, understanding why golang fails here