Skip to content

Context

The context package provides a mechanism for carrying deadlines, cancelation signals, and request-scoped values across API boundaries and between processes. It is designed to facilitate the management of long-running processes, such as handling HTTP requests, canceling operations, or setting timeouts.

context.Context

This is the primary type representing an incoming request or task. It allows the propagation of deadlines, cancelation signals, and key-value pairs across API boundaries.

context.WithCancel

This function returns a derived Context and a cancel function. Calling the cancel function signals that the work is done, and all the operations started from this Context should be canceled.

context.WithDeadline

This function returns a derived Context and a cancel function. It is similar to context.WithTimeout, but it allows you to specify an absolute deadline instead of a relative one.

context.WithTimeout

This function returns a derived Context and a cancel function. It is commonly used to set a timeout for an operation. After the specified duration, the cancel function is automatically called.

context.WithValue

This function returns a derived Context with the provided key-value pair. It is used to pass request-scoped values across API boundaries. Normally, is passed metadata.

HTTP Server using all context possible

run command
01. go run src/context/main.go
02. curl localhost:8080/get-context
03. curl localhost:8080/get-context AND CTRL + D to cancell the request before 5s
04. curl localhost:8080/get-context-cancel
05. curl localhost:8080/get-context-cancel?number=2
06. curl localhost:8080/get-context-cancel?number=7
07. curl localhost:8080/get-context-deadline
08. curl localhost:8080/get-context-deadline?number=2
09. curl localhost:8080/get-context-deadline?number=7
10. curl localhost:8080/get-context-timeout
11. curl localhost:8080/get-context-timeout?number=2
12. curl localhost:8080/get-context-timeout?number=7
13. curl localhost:8080/get-context-value
14. curl localhost:8080/get-context-value?number=2
15. curl localhost:8080/get-context-value?number=7
package main

import (
    "context"
    "fmt"
    "log"
    "net/http"
    "strconv"
    "time"
)

func ContextHandler(res http.ResponseWriter, req *http.Request) {
    ctx := req.Context()
    // Could be ctx := context.Background()
    select {
    case <-time.After(5 * time.Second):
        res.WriteHeader(http.StatusOK)
        res.Write([]byte("Context completed successfully."))
        return
    case <-ctx.Done():
        log.Println("Request cancelled by client")
        return
    }
}

func ContextCancelHandler(res http.ResponseWriter, req *http.Request) {
    // Could be ctx := context.Background()
    ctx, cancel := context.WithCancel(req.Context())
    defer cancel()
    queryParams := req.URL.Query()
    number := 0
    if queryParams.Has("number") {
        num, err := strconv.Atoi(queryParams.Get("number"))
        if err != nil {
            res.WriteHeader(http.StatusBadRequest)
            res.Write([]byte("ERROR Parsing number parameter to int"))
            return
        }
        number = num
    }

    if number%2 == 0 {
        res.WriteHeader(http.StatusOK)
        res.Write([]byte("ContextCancel completed successfully."))
        return
    } else {
        cancel()
    }

    select {
    case <-ctx.Done():
        res.WriteHeader(http.StatusBadRequest)
        res.Write([]byte("ContextCancel Task canceled or timed out. Number cannot be odd"))
        return
    }
}

func ContextDeadLineHandler(res http.ResponseWriter, req *http.Request) {
    d := time.Now().Add(5 * time.Second)
    ctx, cancel := context.WithDeadline(req.Context(), d)
    defer cancel()
    queryParams := req.URL.Query()
    number := 0
    if queryParams.Has("number") {
        num, err := strconv.Atoi(queryParams.Get("number"))
        if err != nil {
            res.WriteHeader(http.StatusBadRequest)
            res.Write([]byte("ERROR Parsing number parameter to int"))
            return
        }
        number = num
    }

    time.Sleep(time.Duration(number) * time.Second)
    select {
    case <-ctx.Done():
        res.WriteHeader(http.StatusBadRequest)
        res.Write([]byte("ContextDeadLineHandler Task canceled or timed out."))
    default:
        res.WriteHeader(http.StatusOK)
        res.Write([]byte("ContextDeadLineHandler completed successfully."))
    }
    return
}

func ContextTimeoutHandler(res http.ResponseWriter, req *http.Request) {
    // Could be ctx := context.Background()
    ctx, cancel := context.WithTimeout(req.Context(), 3*time.Second)
    defer cancel()

    queryParams := req.URL.Query()
    number := 0
    if queryParams.Has("number") {
        num, err := strconv.Atoi(queryParams.Get("number"))
        if err != nil {
            res.WriteHeader(http.StatusBadRequest)
            res.Write([]byte("ERROR Parsing number parameter to int"))
            return
        }
        number = num
    }

    select {
    case <-time.After(time.Duration(number) * time.Second):
        res.WriteHeader(http.StatusOK)
        res.Write([]byte("ContextTimeout completed successfully."))
        return
    case <-ctx.Done():
        res.WriteHeader(http.StatusRequestTimeout)
        res.Write([]byte("ContextTimeout Task canceled or timed out."))
        return
    }
}

func ContextValueHandler(res http.ResponseWriter, req *http.Request) {
    queryParams := req.URL.Query()
    number := 0
    if queryParams.Has("number") {
        num, err := strconv.Atoi(queryParams.Get("number"))
        if err != nil {
            res.WriteHeader(http.StatusBadRequest)
            res.Write([]byte("ERROR Parsing number parameter to int"))
            return
        }
        number = num
    }
    parentCtx, cancel := context.WithCancel(req.Context())
    defer cancel()
    // Could be ctx := context.Background()
    ctx := context.WithValue(parentCtx, "number", number)
    fmt.Println("Number passed", ctx.Value("number"))

    if number%2 == 0 {
        res.WriteHeader(http.StatusOK)
        res.Write([]byte("ContextValue completed successfully"))
        return
    } else {
        cancel()
    }

    select {
    case <-ctx.Done():
        res.WriteHeader(http.StatusBadRequest)
        res.Write([]byte("ContextValue canceled or timed out. Number cannot be odd"))
        return
    }
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/get-context", ContextHandler)
    mux.HandleFunc("/get-context-cancel", ContextCancelHandler)
    mux.HandleFunc("/get-context-deadline", ContextDeadLineHandler)
    mux.HandleFunc("/get-context-timeout", ContextTimeoutHandler)
    mux.HandleFunc("/get-context-value", ContextValueHandler)

    server := &http.Server{
        Addr:    ":8080",
        Handler: mux,
    }
    log.Println("Server is listening on port 8080")
    if err := server.ListenAndServe(); err != nil {
        log.Fatal(err)
    }
}
output
1. 2023/10/30 18:28:01 Server is listening on port 8080
2. Context completed successfully.
3. 2023/10/30 18:30:15 Request cancelled by client
4. ContextCancel completed successfully
5. ContextCancel completed successfully
6. ContextCancel Task canceled or timed out. Number cannot be odd
7. ContextDeadLineHandler completed successfully.
8. ContextDeadLineHandler completed successfully.
9. ContextDeadLineHandler Task canceled or timed out.
10. ContextTimeout completed successfully.
11. ContextTimeout completed successfully.
12. ContextTimeout Task canceled or timed out.
13. ContextValue completed successfully.
13.1 Number passed 0
14. ContextValue completed successfully.
14.1 Number passed 2
15. ContextValue canceled or timed out. Number cannot be odd.
15.1 Number passed 7