3

I stumbled accross something that threw me for a loop in golang recently.

package main

import (
    "net/http"
    "bytes"
    "fmt"
    "io/ioutil"
)

func createRequest(method, uri string, data *bytes.Buffer) string {
    req, err := http.NewRequest(method, uri, data)
    if err != nil {
       return ""
    }
    req.Close = true
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
       return ""
    }
    defer resp.Body.Close()
    respBody, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return ""
    }
    return string(respBody)
}

func createRequestNoBody(method, uri string) string {
    req, err := http.NewRequest(method, uri, nil)
    if err != nil {
       return ""
    }
    req.Close = true
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
       return ""
    }
    defer resp.Body.Close()
    respBody, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return ""
    }
    return string(respBody)
}

func main() {
   data := createRequestNoBody("GET", "http://google.com")
   if len(data) != 0 {
      fmt.Println("First Request Ok")
   }
   data = createRequest("GET", "https://google.com", nil)
   if len(data) != 0 {
      fmt.Println("Second Request OK")
   }
}

This code works in the createRequestNoBody function when nil is passed directly to http.NewRequest but it produces a panic when calling createRequest with the *bytes.Buffer set to nil in the calling function.

The NewRequest function in the http library has the following prototype:

func NewRequest(method, urlStr string, body io.Reader) (*Request, error)

Where io.Reader is an interface.

I'm not sure why the nil being passed through a variable would cause body in NewRequest to become non nil.

Can anyone explain why createRequest method results in a panic? How is the nil parameter at data = createRequest("GET", "https://google.com", nil) becoming non nil inside of the NewRequest method?

soluwalana
  • 68
  • 7
  • 3
    See the [FAQ: Why is my nil error value not equal to nil?](https://golang.org/doc/faq#nil_error) – JimB Sep 21 '16 at 01:34
  • 4
    Possible duplicate of [Hiding nil values, understanding why golang fails here](http://stackoverflow.com/questions/29138591/hiding-nil-values-understanding-why-golang-fails-here) – icza Sep 21 '16 at 01:36
  • Thanks for the link @JimB, That does indeed explain it. When it gets passed between the createRequest function it gains a type in the interface and that is passed through to the next function as (*bytes.Buffer, nil) instead of (nil, nil). Is the value of `data` before the call to NewRequest (nil, nil) or (*bytes.Buffer, nil). I'm not really sure when it becomes (*bytes.Buffer, nil) is the problem. – soluwalana Sep 21 '16 at 01:59
  • 2
    `data` is `nil`, but has the type `*bytes.Buffer` – JimB Sep 21 '16 at 02:04

1 Answers1

0

@JimB was correct in his comment. This snippet of code below shows that the type of the object is indeed (*bytes.Buffer, nil)

package main

import (
    "bytes"
    "fmt"
    "reflect"
    "errors"
    "io"
)
func NewRequest(method, urlStr string, body io.Reader) (error) {
    if body == nil {
       fmt.Println("Body Is Nil")
       return nil
    }
    fmt.Println(reflect.TypeOf(body).Elem())
    return errors.New("NOT NIL")
}

func createRequest(method, uri string, data *bytes.Buffer) string {
    fmt.Println("Type Of data is ", reflect.TypeOf(data).Elem())

    if data == nil {
       fmt.Println("data is nil")
    }
    err := NewRequest(method, uri, data)
    if err != nil {
       return err.Error()
    }
    return "OK"
}

func createRequestNoBody(method, uri string) string {
    err := NewRequest(method, uri, nil)
    if err != nil {
       return err.Error()
    }
    return "OK"
}

func main() {
   data := createRequestNoBody("GET", "http://google.com")
   if len(data) != 0 {
      fmt.Println("First Request Ok")
   }
   data = createRequest("GET", "https://google.com", nil)
   if len(data) != 0 {
      fmt.Println("Second Request Not OK")
   }
}

The output of the run looks like:

Body Is Nil
First Request Ok
Type Of data is  bytes.Buffer
data is nil
bytes.Buffer
Second Request Not OK

Since body io.Reader is an interface and not a pointer to a particular type when you pass in a nil pointer of type bytes.Buffer it will be a non nil interface.

soluwalana
  • 68
  • 7