Search code examples
c++linuxgosystemd

GoLang(cgp) http.Client get request hangs from Linux daemon process


I have written a simple GoLang function which makes HTTP GET request using http.Client and prints the response string. I then exported it as C function in .so file using following command.

go build -o libapp.so -buildmode=c-shared libapp.go

Then using this .SO and .h, called exported Go function from my C test program. Below is the command I used to compile and build my C test program.

gcc -o goclient goclient.c ./libapp.so

Here is CheckConnection function in GoLang

var file io.Writer

//export CheckConnection
func CheckConnection() {

path := "/opt/logs/gologs.txt"
f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
    log.Fatal(err)    
}
file = f
log.SetOutput(file)
log.Print("---------Logging started---------")
log.Print("Hello from CheckConnection\r\n")

log.Print("CheckConnection creating http.Client: step1")

var netTransport = &http.Transport{
  Dial: (&net.Dialer{
Timeout: 10 * time.Second,
  }).Dial,
  TLSHandshakeTimeout: 10 * time.Second,    
}
log.Print("CheckConnection creating http.Client: step2")

var myClient = &http.Client{
  Timeout: time.Second * 10,
  Transport: netTransport,
}
log.Print("CheckConnection creating http.Client: step3")

res,err := myClient.Get("https://www.google.com")
log.Print("CheckConnection creating http.Client: step4")
if err!= nil {
log.Print("Error ",err)
}
log.Print("CheckConnection creating http.Client: step5")
data,_ := ioutil.ReadAll(res.Body)
log.Print("CheckConnection creating http.Client: step6")
res.Body.Close()
log.Print("CheckConnection creating http.Client: step7")
log.Print("Resp code ",res.StatusCode)
log.Print("Data ",data)

defer log.Print("Returning from CheckConnection");

}

Thread gets blocked at below line, does not timeout or gives error -

 res,err := myClient.Get("https://www.google.com")

This happens when CheckConnection() function gets called from Daemon process. If I call this function from C,C++ main() process then it works. I have reported same issue on GitHub https://github.com/golang/go/issues/47077. It has sample code to reproduce the issue (Sample.zip).

Please refer to GitHub issue https://github.com/golang/go/issues/47077 for sample reproducible code.

Adding go env below

GO111MODULE=""
GOARCH="386"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOENV="/root/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="386"
GOHOSTOS="linux"
GOINSECURE="golang.org,googlesource.com"
GOMODCACHE="/home/nilesh/golang/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/nilesh/golang"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_386"
GOVCS=""
GOVERSION="go1.16.4"
GCCGO="gccgo"
GO386="sse2"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/nilesh/SVN/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m32 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build718813889=/tmp/go-build -gno-record-gcc-switches"

Solution

  • When using cgp shared library in your C/C++ program on linux and if you are calling these functions from forked process then make sure you should consider below points.

    1. Do not statically link the shared library, instead load it dynamically using dlopen, dlsym, dlclose.

    2. Make sure while doing above you are not providing shared lib name during linking

    This solved my problem and I am able to to make network calls from my forked process.