Golang Context WithTimeout Example
How do we implement a context with a given timeout in Go (Golang)? Thanksfully Go has a lot of functionality built-in into the standard library and in this article we are going to see how it’s possible to define a timeout with the context package.
Golang HTTP Request with Context
Let’s first explore a scenario, we have an HTTP request and we want to control the timeout of such request.
package main
import "net/http"
import "log"
import "io/ioutil"
func main () {
req, err := http.NewRequest(http.MethodGet, "http://httpbin.org/get", nil)
if err != nil {
log.Fatal(err)
}
c := &http.Client{}
res, err := c.Do(req)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
out, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Fatal(err)
}
log.Println(string(out))
}
Ok, what if we want to make sure this request completes within 80ms? We just need to use context.WithTimeout!
Here’s the addition we need to do to our code sample
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(time.Millisecond*80))
defer cancel()
req = req.WithContext(ctx)
We first define a new context specifying a timeout (using time.Duration). We then add the context to our request using WithContext. When creating our new context with timeout we receive a CancelFunc which is used to cleanup resources once the context expires. Here’s more info about the CancelFunc
Calling the CancelFunc cancels the child and its children, removes the parent’s reference to the child, and stops any associated timers. Failing to call the CancelFunc leaks the child and its children until the parent is canceled or the timer fires. The go vet tool checks that CancelFuncs are used on all control-flow paths.
You can also define a http request with a context using NewRequestWithContext
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(time.Millisecond*80))
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://httpbin.org/get", nil)
Here’s the final example
package main
import "net/http"
import "log"
import "io/ioutil"
import "context"
import "time"
func main () {
req, err := http.NewRequest(http.MethodGet, "http://httpbin.org/get", nil)
if err != nil {
log.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(time.Millisecond*80))
defer cancel()
req = req.WithContext(ctx)
c := &http.Client{}
res, err := c.Do(req)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
out, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Fatal(err)
}
log.Println(string(out))
}
This will automatically timeout and cancel the request after 80 milliseconds