Skip to content

Instantly share code, notes, and snippets.

@dmichael
Last active October 18, 2023 20:07

Revisions

  1. dmichael revised this gist Jun 5, 2013. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions httpclient.go
    Original file line number Diff line number Diff line change
    @@ -16,10 +16,10 @@ func TimeoutDialer(config *Config) func(net, addr string) (c net.Conn, err 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 {
  2. dmichael revised this gist Jun 5, 2013. 1 changed file with 31 additions and 31 deletions.
    62 changes: 31 additions & 31 deletions httpclient.go
    Original file line number Diff line number Diff line change
    @@ -1,49 +1,49 @@
    package httpclient

    import (
    "net"
    "net/http"
    "time"
    "net"
    "net/http"
    "time"
    )

    type Config struct {
    ConnectTimeout time.Duration
    ReadWriteTimeout time.Duration
    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
    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
    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,
    }
    // 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
    }
    // 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)
    }
    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),
    },
    }
    return &http.Client{
    Transport: &http.Transport{
    Dial: TimeoutDialer(config),
    },
    }
    }
  3. dmichael revised this gist Jun 5, 2013. 2 changed files with 2 additions and 2 deletions.
    2 changes: 1 addition & 1 deletion httpclient.go
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    package httpclient

    import (
    "net"
    "net"
    "net/http"
    "time"
    )
    2 changes: 1 addition & 1 deletion httpclient_test.go
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    package httpclient

    import (
    "io"
    "io"
    "net"
    "net/http"
    "sync"
  4. dmichael revised this gist Jun 5, 2013. 1 changed file with 7 additions and 2 deletions.
    9 changes: 7 additions & 2 deletions httpclient_usage.go
    Original file line number Diff line number Diff line change
    @@ -2,17 +2,22 @@
    This wrapper takes care of both the connection timeout and the readwrite timeout.
    WARNING: You must instantiate this every time you want to use it, otherwise it is
    likely that the timeout is reached before you actually make the call.
    One argument sets the connect timeout and the readwrite timeout to the same value.
    Other wise, 2 arguments are 1) connect and 2) readwrite
    It returns an *http.Client
    */

    package main

    import(
    "httpclient"
    "time
    "time"
    )

    func main() {
    httpClient := httpclient.NewWithTimeout(500*time.Millisecond)
    httpClient := httpclient.NewWithTimeout(500*time.Millisecond, 1*time.Second)
    resp, err := httpClient.Get("http://google.com")
    if err != nil {
    fmt.Println("Rats! Google is down.")
  5. dmichael revised this gist Jun 5, 2013. 1 changed file with 20 additions and 0 deletions.
    20 changes: 20 additions & 0 deletions httpclient_usage.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,20 @@
    /*
    This wrapper takes care of both the connection timeout and the readwrite timeout.
    WARNING: You must instantiate this every time you want to use it, otherwise it is
    likely that the timeout is reached before you actually make the call.
    */

    package main

    import(
    "httpclient"
    "time
    )

    func main() {
    httpClient := httpclient.NewWithTimeout(500*time.Millisecond)
    resp, err := httpClient.Get("http://google.com")
    if err != nil {
    fmt.Println("Rats! Google is down.")
    }
    }
  6. dmichael created this gist Jun 5, 2013.
    49 changes: 49 additions & 0 deletions httpclient.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,49 @@
    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),
    },
    }
    }
    84 changes: 84 additions & 0 deletions httpclient_test.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,84 @@
    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")
    }

    }