Last active
September 26, 2018 17:19
-
-
Save davrodpin/6d0e7cbd8aea477a7990f9ba3e5d3692 to your computer and use it in GitHub Desktop.
Golang http.Client bug when switching linux namespace before performing request.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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