1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
# github.com/lestrrat-go/httprc  [](https://pkg.go.dev/github.com/lestrrat-go/httprc) [](https://codecov.io/github/lestrrat-go/httprc)
`httprc` is a HTTP "Refresh" Cache. Its aim is to cache a remote resource that
can be fetched via HTTP, but keep the cached content up-to-date based on periodic
refreshing.
# SYNOPSIS
<!-- INCLUDE(httprc_example_test.go) -->
```go
package httprc_test
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"sync"
"time"
"github.com/lestrrat-go/httprc"
)
const (
helloWorld = `Hello World!`
goodbyeWorld = `Goodbye World!`
)
func ExampleCache() {
var mu sync.RWMutex
msg := helloWorld
srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set(`Cache-Control`, fmt.Sprintf(`max-age=%d`, 2))
w.WriteHeader(http.StatusOK)
mu.RLock()
fmt.Fprint(w, msg)
mu.RUnlock()
}))
defer srv.Close()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
errSink := httprc.ErrSinkFunc(func(err error) {
fmt.Printf("%s\n", err)
})
c := httprc.NewCache(ctx,
httprc.WithErrSink(errSink),
httprc.WithRefreshWindow(time.Second), // force checks every second
)
c.Register(srv.URL,
httprc.WithHTTPClient(srv.Client()), // we need client with TLS settings
httprc.WithMinRefreshInterval(time.Second), // allow max-age=1 (smallest)
)
payload, err := c.Get(ctx, srv.URL)
if err != nil {
fmt.Printf("%s\n", err)
return
}
if string(payload.([]byte)) != helloWorld {
fmt.Printf("payload mismatch: %s\n", payload)
return
}
mu.Lock()
msg = goodbyeWorld
mu.Unlock()
time.Sleep(4 * time.Second)
payload, err = c.Get(ctx, srv.URL)
if err != nil {
fmt.Printf("%s\n", err)
return
}
if string(payload.([]byte)) != goodbyeWorld {
fmt.Printf("payload mismatch: %s\n", payload)
return
}
cancel()
// OUTPUT:
}
```
source: [httprc_example_test.go](https://github.com/lestrrat-go/jwx/blob/refs/heads/main/httprc_example_test.go)
<!-- END INCLUDE -->
# Sequence Diagram
```mermaid
sequenceDiagram
autonumber
actor User
participant httprc.Cache
participant httprc.Storage
User->>httprc.Cache: Fetch URL `u`
activate httprc.Storage
httprc.Cache->>httprc.Storage: Fetch local cache for `u`
alt Cache exists
httprc.Storage-->httprc.Cache: Return local cache
httprc.Cache-->>User: Return data
Note over httprc.Storage: If the cache exists, there's nothing more to do.<br />The cached content will be updated periodically in httprc.Refresher
deactivate httprc.Storage
else Cache does not exist
activate httprc.Fetcher
httprc.Cache->>httprc.Fetcher: Fetch remote resource `u`
httprc.Fetcher-->>httprc.Cache: Return fetched data
deactivate httprc.Fetcher
httprc.Cache-->>User: Return data
httprc.Cache-)httprc.Refresher: Enqueue into auto-refresh queue
activate httprc.Refresher
loop Refresh Loop
Note over httprc.Storage,httprc.Fetcher: Cached contents are updated synchronously
httprc.Refresher->>httprc.Refresher: Wait until next refresh
httprc.Refresher-->>httprc.Fetcher: Request fetch
httprc.Fetcher->>httprc.Refresher: Return fetched data
httprc.Refresher-->>httprc.Storage: Store new version in cache
httprc.Refresher->>httprc.Refresher: Enqueue into auto-refresh queue (again)
end
deactivate httprc.Refresher
end
```
|