Skip to content

Instantly share code, notes, and snippets.

@decoomanj
Forked from seantalts/httpclient.go
Created October 11, 2017 10:33
Show Gist options
  • Select an option

  • Save decoomanj/c85e5f6540936b7c6b32ecc59a0c3e95 to your computer and use it in GitHub Desktop.

Select an option

Save decoomanj/c85e5f6540936b7c6b32ecc59a0c3e95 to your computer and use it in GitHub Desktop.
package httpclient
import (
"net"
"net/http"
"time"
)
type Config struct {
ConnectTimeout time.Duration
ReadWriteTimeout time.Duration
}
func TimeoutDialer(config *Config) func(net, addr string) (c net.Conn, err error) {
return func(netw, addr string) (net.Conn, error) {
conn, err := net.DialTimeout(netw, addr, config.ConnectTimeout)
if err != nil {
return nil, err
}
conn.SetDeadline(time.Now().Add(config.ReadWriteTimeout))
return conn, nil
}
}
func NewTimeoutClient(args ...interface{}) *http.Client {
// Default configuration
config := &Config{
ConnectTimeout: 1 * time.Second,
ReadWriteTimeout: 1 * time.Second,
}
// merge the default with user input if there is one
if len(args) == 1 {
timeout := args[0].(time.Duration)
config.ConnectTimeout = timeout
config.ReadWriteTimeout = timeout
}
if len(args) == 2 {
config.ConnectTimeout = args[0].(time.Duration)
config.ReadWriteTimeout = args[1].(time.Duration)
}
return &http.Client{
Transport: &http.Transport{
Dial: TimeoutDialer(config),
},
}
}
package httpclient
import (
"io"
"net"
"net/http"
"sync"
"testing"
"time"
)
var starter sync.Once
var addr net.Addr
func testHandler(w http.ResponseWriter, req *http.Request) {
time.Sleep(500 * time.Millisecond)
io.WriteString(w, "hello, world!\n")
}
func testDelayedHandler(w http.ResponseWriter, req *http.Request) {
time.Sleep(2100 * time.Millisecond)
io.WriteString(w, "hello, world ... in a bit\n")
}
func setupMockServer(t *testing.T) {
http.HandleFunc("/test", testHandler)
http.HandleFunc("/test-delayed", testDelayedHandler)
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 TestDefaultConfig(t *testing.T) {
starter.Do(func() { setupMockServer(t) })
httpClient := NewTimeoutClient()
req, _ := http.NewRequest("GET", "http://"+addr.String()+"/test-delayed", nil)
httpClient = NewTimeoutClient()
_, err := httpClient.Do(req)
if err == nil {
t.Fatalf("request should have timed out")
}
}
func TestHttpClient(t *testing.T) {
starter.Do(func() { setupMockServer(t) })
httpClient := NewTimeoutClient()
req, _ := http.NewRequest("GET", "http://"+addr.String()+"/test", nil)
resp, err := httpClient.Do(req)
if err != nil {
t.Fatalf("1st request failed - %s", err.Error())
}
defer resp.Body.Close()
connectTimeout := (250 * time.Millisecond)
readWriteTimeout := (50 * time.Millisecond)
httpClient = NewTimeoutClient(connectTimeout, readWriteTimeout)
resp, err = httpClient.Do(req)
if err == nil {
t.Fatalf("2nd request should have timed out")
}
resp, err = httpClient.Do(req)
if resp != nil {
t.Fatalf("3nd request should not have timed out")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment