|
|
@@ -0,0 +1,233 @@
|
|
|
+package httpclient
|
|
|
+
|
|
|
+import (
|
|
|
+ "crypto/tls"
|
|
|
+ "io"
|
|
|
+ "io/ioutil"
|
|
|
+ "net"
|
|
|
+ "net/http"
|
|
|
+ "sync"
|
|
|
+ "testing"
|
|
|
+ "time"
|
|
|
+)
|
|
|
+
|
|
|
+var starter sync.Once
|
|
|
+var addr net.Addr
|
|
|
+
|
|
|
+func testHandler(w http.ResponseWriter, req *http.Request) {
|
|
|
+ time.Sleep(200 * time.Millisecond)
|
|
|
+ io.WriteString(w, "hello, world!\n")
|
|
|
+}
|
|
|
+
|
|
|
+func postHandler(w http.ResponseWriter, req *http.Request) {
|
|
|
+ ioutil.ReadAll(req.Body)
|
|
|
+ w.Header().Set("Content-Length", "2")
|
|
|
+ io.WriteString(w, "OK")
|
|
|
+}
|
|
|
+
|
|
|
+func closeHandler(w http.ResponseWriter, req *http.Request) {
|
|
|
+ hj, _ := w.(http.Hijacker)
|
|
|
+ conn, bufrw, _ := hj.Hijack()
|
|
|
+ defer conn.Close()
|
|
|
+ bufrw.WriteString("HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n")
|
|
|
+ bufrw.Flush()
|
|
|
+}
|
|
|
+
|
|
|
+func redirectHandler(w http.ResponseWriter, req *http.Request) {
|
|
|
+ ioutil.ReadAll(req.Body)
|
|
|
+ http.Redirect(w, req, "/post", 302)
|
|
|
+}
|
|
|
+
|
|
|
+func redirect2Handler(w http.ResponseWriter, req *http.Request) {
|
|
|
+ ioutil.ReadAll(req.Body)
|
|
|
+ http.Redirect(w, req, "/redirect", 302)
|
|
|
+}
|
|
|
+
|
|
|
+func slowHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
+ w.WriteHeader(200)
|
|
|
+ io.WriteString(w, "START\n")
|
|
|
+ f := w.(http.Flusher)
|
|
|
+ f.Flush()
|
|
|
+ time.Sleep(200 * time.Millisecond)
|
|
|
+ io.WriteString(w, "WORKING\n")
|
|
|
+ f.Flush()
|
|
|
+ time.Sleep(200 * time.Millisecond)
|
|
|
+ io.WriteString(w, "DONE\n")
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func setupMockServer(t *testing.T) {
|
|
|
+ http.HandleFunc("/test", testHandler)
|
|
|
+ http.HandleFunc("/post", postHandler)
|
|
|
+ http.HandleFunc("/redirect", redirectHandler)
|
|
|
+ http.HandleFunc("/redirect2", redirect2Handler)
|
|
|
+ http.HandleFunc("/close", closeHandler)
|
|
|
+ http.HandleFunc("/slow", slowHandler)
|
|
|
+ ln, err := net.Listen("tcp", ":0")
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("failed to listen - %s", err.Error())
|
|
|
+ }
|
|
|
+ go func() {
|
|
|
+ err = http.Serve(ln, nil)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("failed to start HTTP server - %s", err.Error())
|
|
|
+ }
|
|
|
+ }()
|
|
|
+ addr = ln.Addr()
|
|
|
+}
|
|
|
+
|
|
|
+func TestHttpsConnection(t *testing.T) {
|
|
|
+ transport := &Transport{
|
|
|
+ ConnectTimeout: 1 * time.Second,
|
|
|
+ RequestTimeout: 2 * time.Second,
|
|
|
+ TLSClientConfig: &tls.Config{
|
|
|
+ InsecureSkipVerify: true,
|
|
|
+ },
|
|
|
+ }
|
|
|
+ defer transport.Close()
|
|
|
+ client := &http.Client{Transport: transport}
|
|
|
+
|
|
|
+ req, _ := http.NewRequest("GET", "https://httpbin.org/ip", nil)
|
|
|
+ resp, err := client.Do(req)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("1st request failed - %s", err.Error())
|
|
|
+ }
|
|
|
+ _, err = ioutil.ReadAll(resp.Body)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("1st failed to read body - %s", err.Error())
|
|
|
+ }
|
|
|
+ resp.Body.Close()
|
|
|
+
|
|
|
+ req2, _ := http.NewRequest("GET", "https://httpbin.org/delay/5", nil)
|
|
|
+ _, err = client.Do(req2)
|
|
|
+ if err == nil {
|
|
|
+ t.Fatalf("HTTPS request should have timed out")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestHttpClient(t *testing.T) {
|
|
|
+ starter.Do(func() { setupMockServer(t) })
|
|
|
+
|
|
|
+ transport := &Transport{
|
|
|
+ ConnectTimeout: 1 * time.Second,
|
|
|
+ RequestTimeout: 5 * time.Second,
|
|
|
+ ReadWriteTimeout: 3 * time.Second,
|
|
|
+ }
|
|
|
+ client := &http.Client{Transport: transport}
|
|
|
+
|
|
|
+ req, _ := http.NewRequest("GET", "http://"+addr.String()+"/test", nil)
|
|
|
+ resp, err := client.Do(req)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("1st request failed - %s", err.Error())
|
|
|
+ }
|
|
|
+ _, err = ioutil.ReadAll(resp.Body)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("1st failed to read body - %s", err.Error())
|
|
|
+ }
|
|
|
+ resp.Body.Close()
|
|
|
+ transport.Close()
|
|
|
+
|
|
|
+ transport = &Transport{
|
|
|
+ ConnectTimeout: 25 * time.Millisecond,
|
|
|
+ RequestTimeout: 50 * time.Millisecond,
|
|
|
+ ReadWriteTimeout: 50 * time.Millisecond,
|
|
|
+ }
|
|
|
+ client = &http.Client{Transport: transport}
|
|
|
+
|
|
|
+ req2, _ := http.NewRequest("GET", "http://"+addr.String()+"/test", nil)
|
|
|
+ resp, err = client.Do(req2)
|
|
|
+ if err == nil {
|
|
|
+ t.Fatal("2nd request should have timed out")
|
|
|
+ }
|
|
|
+ transport.Close()
|
|
|
+
|
|
|
+ transport = &Transport{
|
|
|
+ ConnectTimeout: 25 * time.Millisecond,
|
|
|
+ RequestTimeout: 250 * time.Millisecond,
|
|
|
+ ReadWriteTimeout: 250 * time.Millisecond,
|
|
|
+ }
|
|
|
+ client = &http.Client{Transport: transport}
|
|
|
+
|
|
|
+ req3, _ := http.NewRequest("GET", "http://"+addr.String()+"/test", nil)
|
|
|
+ resp, err = client.Do(req3)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatal("3rd request should not have timed out")
|
|
|
+ }
|
|
|
+ resp.Body.Close()
|
|
|
+ transport.Close()
|
|
|
+}
|
|
|
+
|
|
|
+func TestSlowServer(t *testing.T) {
|
|
|
+ starter.Do(func() { setupMockServer(t) })
|
|
|
+
|
|
|
+ transport := &Transport{
|
|
|
+ ConnectTimeout: 25 * time.Millisecond,
|
|
|
+ RequestTimeout: 500 * time.Millisecond,
|
|
|
+ ReadWriteTimeout: 250 * time.Millisecond,
|
|
|
+ }
|
|
|
+
|
|
|
+ client := &http.Client{Transport: transport}
|
|
|
+
|
|
|
+ req, _ := http.NewRequest("GET", "http://"+addr.String()+"/slow", nil)
|
|
|
+ resp, err := client.Do(req)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("1st request failed - %s", err)
|
|
|
+ }
|
|
|
+ _, err = ioutil.ReadAll(resp.Body)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("1st failed to read body - %s", err)
|
|
|
+ }
|
|
|
+ resp.Body.Close()
|
|
|
+ transport.Close()
|
|
|
+
|
|
|
+ transport = &Transport{
|
|
|
+ ConnectTimeout: 25 * time.Millisecond,
|
|
|
+ RequestTimeout: 500 * time.Millisecond,
|
|
|
+ ReadWriteTimeout: 100 * time.Millisecond,
|
|
|
+ }
|
|
|
+ client = &http.Client{Transport: transport}
|
|
|
+
|
|
|
+ req, _ = http.NewRequest("GET", "http://"+addr.String()+"/slow", nil)
|
|
|
+ resp, err = client.Do(req)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("2nd request failed - %s", err)
|
|
|
+ }
|
|
|
+ _, err = ioutil.ReadAll(resp.Body)
|
|
|
+ netErr, ok := err.(net.Error)
|
|
|
+ if !ok {
|
|
|
+ t.Fatalf("2nd request dind't return a net.Error - %s", netErr)
|
|
|
+ }
|
|
|
+
|
|
|
+ if !netErr.Timeout() {
|
|
|
+ t.Fatalf("2nd request should have timed out - %s", netErr)
|
|
|
+ }
|
|
|
+
|
|
|
+ resp.Body.Close()
|
|
|
+ transport.Close()
|
|
|
+}
|
|
|
+
|
|
|
+func TestMultipleRequests(t *testing.T) {
|
|
|
+ starter.Do(func() { setupMockServer(t) })
|
|
|
+
|
|
|
+ transport := &Transport{
|
|
|
+ ConnectTimeout: 1 * time.Second,
|
|
|
+ RequestTimeout: 5 * time.Second,
|
|
|
+ ReadWriteTimeout: 3 * time.Second,
|
|
|
+ ResponseHeaderTimeout: 400 * time.Millisecond,
|
|
|
+ }
|
|
|
+ client := &http.Client{Transport: transport}
|
|
|
+
|
|
|
+ req, _ := http.NewRequest("GET", "http://"+addr.String()+"/test", nil)
|
|
|
+ for i := 0; i < 10; i++ {
|
|
|
+ resp, err := client.Do(req)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("%d request failed - %s", i, err.Error())
|
|
|
+ }
|
|
|
+ _, err = ioutil.ReadAll(resp.Body)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("%d failed to read body - %s", i, err.Error())
|
|
|
+ }
|
|
|
+ resp.Body.Close()
|
|
|
+ }
|
|
|
+ transport.Close()
|
|
|
+}
|