Skip to content

Instantly share code, notes, and snippets.

@davrodpin
Last active September 26, 2018 17:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davrodpin/6d0e7cbd8aea477a7990f9ba3e5d3692 to your computer and use it in GitHub Desktop.
Save davrodpin/6d0e7cbd8aea477a7990f9ba3e5d3692 to your computer and use it in GitHub Desktop.
Golang http.Client bug when switching linux namespace before performing request.
package main
import (
"bufio"
"fmt"
"net"
"net/http"
"os/exec"
"runtime"
"github.com/vishvananda/netns"
)
const (
serverAddress = "127.0.0.1:8080"
)
func main() {
origin, err := netns.Get()
if err != nil {
panic(fmt.Sprintf("can't get information about current linux namespace: %v", err))
}
defer origin.Close()
ns, err := netns.GetFromName("gobug")
if err != nil {
panic(err)
}
defer ns.Close()
runtime.LockOSThread()
defer runtime.UnlockOSThread()
err = netns.Set(ns)
if err != nil {
panic(fmt.Sprintf("can't switch to linux namespace 'gobug': %v", err))
}
fmt.Println("switching linux namespace to 'gobug'")
defer func() {
netns.Set(origin)
fmt.Println("switching linux namespace to previous one")
}()
if err = RequestUsingCurl(); err != nil {
fmt.Printf("%v\n", err)
}
if err = RequestUsingNetDial(); err != nil {
fmt.Printf("%v\n", err)
}
if err = RequestUsingHttpClient(); err != nil {
fmt.Printf("%v\n", err)
}
}
func RequestUsingHttpClient() error {
if _, err := http.Get(fmt.Sprintf("http://%s", serverAddress)); err != nil {
return fmt.Errorf("error while sending http request using http.Client: %v", err)
}
fmt.Println("http.Client is working as expected")
return nil
}
func RequestUsingNetDial() error {
conn, err := net.Dial("tcp", serverAddress)
if err != nil {
return fmt.Errorf("error while establishing tcp connection with server: %v", err)
}
defer conn.Close()
if _, err = conn.Write([]byte("GET /foo HTTP/1.0\n\n")); err != nil {
return fmt.Errorf("error while sending http request using net.Dial: %v", err)
}
resp, _ := bufio.NewReader(conn).ReadString('\n')
fmt.Printf("request to http server using net.Dial returned with success: %s", resp)
return nil
}
func RequestUsingCurl() error {
cmd := exec.Command("curl", fmt.Sprintf("http://%s/foo", serverAddress))
out, err := cmd.Output()
if err != nil {
return fmt.Errorf("error while executing http request using curl: %v", err)
}
fmt.Printf("request to http server using exec.Command(curl) returned with success: %s\n", out)
return nil
}
package main
import (
"context"
"fmt"
"net"
"net/http"
"runtime"
"time"
"github.com/vishvananda/netns"
)
const (
serverAddress = "127.0.0.1:8080"
)
func main() {
origin, err := netns.Get()
if err != nil {
panic(fmt.Sprintf("can't get information about current linux namespace: %v", err))
}
defer origin.Close()
ns, err := netns.GetFromName("gobug")
if err != nil {
panic(err)
}
defer ns.Close()
if err = RequestUsingHttpClient(func() {
// Setup
err := netns.Set(ns)
if err != nil {
panic(fmt.Sprintf("can't switch to linux namespace 'gobug': %v", err))
}
}, func() {
// Teardown
netns.Set(origin)
}); err != nil {
fmt.Printf("%v\n", err)
}
}
func RequestUsingHttpClient(setup, teardown func()) error {
defer teardown()
defer runtime.UnlockOSThread()
c := http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
runtime.LockOSThread()
setup()
return (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext(ctx, network, address)
},
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
},
}
if _, err := c.Get(fmt.Sprintf("http://%s", serverAddress)); err != nil {
return fmt.Errorf("error while sending http request using http.Client: %v", err)
}
fmt.Println("http.Client is working as expected")
return nil
}
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %q", r.URL.Path)
})
log.Fatal(http.ListenAndServe(":8080", nil))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment