Skip to content

Commit 6d56705

Browse files
fix http.Transport
1 parent b088635 commit 6d56705

4 files changed

Lines changed: 128 additions & 42 deletions

File tree

README.md

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -226,12 +226,62 @@ This repository has an http.Transport implementation to use sqlite as cache of H
226226
```go
227227
package main
228228

229-
import httpcache "github.com/walterwanderley/sqlite-http-cache/http"
229+
import (
230+
"context"
231+
"database/sql"
232+
"log/slog"
233+
"time"
234+
235+
_ "github.com/mattn/go-sqlite3"
236+
dbcache "github.com/walterwanderley/sqlite-http-cache/db"
237+
httpcache "github.com/walterwanderley/sqlite-http-cache/http"
238+
)
230239

231240
func main() {
232241

242+
db, err := sql.Open("sqlite3", "file:example.db")
243+
if err != nil {
244+
panic(err)
245+
}
246+
defer db.Close()
247+
248+
tablesNames := []string{"example"}
249+
250+
err = dbcache.CreateResponseTables(db, tablesNames...)
251+
if err != nil {
252+
panic(err)
253+
}
254+
255+
config := httpcache.Config{
256+
DB: db,
257+
Tables: tablesNames,
258+
ReadOnly: false,
259+
TTL: 30 * time.Second,
260+
RFC9111: false,
261+
CleanupInterval: 0,
262+
}
263+
264+
client, err := config.Client(context.Background())
265+
if err != nil {
266+
panic(err)
267+
}
268+
269+
start := time.Now()
270+
resp, err := client.Get("http://swapi.tech/api/films/1")
271+
if err != nil {
272+
panic(err)
273+
}
274+
defer resp.Body.Close()
275+
276+
slog.Info("first request", "duration", time.Since(start))
277+
278+
start = time.Now()
279+
resp, err = client.Get("http://swapi.tech/api/films/1")
280+
if err != nil {
281+
panic(err)
282+
}
283+
defer resp.Body.Close()
284+
285+
slog.Info("second request", "duration", time.Since(start))
233286
}
234-
235-
236-
237287
```

http/http.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,22 @@ type Config struct {
2828
CleanupInterval time.Duration
2929
}
3030

31-
func (c Config) Client(ctx context.Context) (*http.Client, io.Closer, error) {
31+
func (c Config) Client(ctx context.Context) (*http.Client, error) {
3232
cc := internal.ContextClient(ctx)
3333
t, err := newTransport(cc.Transport, c)
3434
if err != nil {
35-
return nil, nil, err
35+
return nil, err
3636
}
3737

38+
go func() {
39+
<-ctx.Done()
40+
t.Close()
41+
}()
42+
3843
return &http.Client{
3944
Transport: t,
4045
CheckRedirect: cc.CheckRedirect,
4146
Jar: cc.Jar,
4247
Timeout: cc.Timeout,
43-
}, t, nil
48+
}, nil
4449
}

http/rw_rfc9111_transport.go

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -54,39 +54,55 @@ func (t *readWriteRFC9111Transport) RoundTrip(req *http.Request) (*http.Response
5454
return t.base.RoundTrip(req)
5555
}
5656

57+
var (
58+
requestTime time.Time
59+
responseTime time.Time
60+
resp *http.Response
61+
err error
62+
)
5763
url := req.URL.String()
5864
respDB, err := t.querier.FindByURL(req.Context(), url)
5965
if err != nil {
60-
requestTime := time.Now()
61-
resp, err := t.base.RoundTrip(req)
66+
requestTime = time.Now()
67+
resp, err = t.base.RoundTrip(req)
6268
if err != nil {
6369
return nil, err
6470
}
65-
responseTime := time.Now()
71+
responseTime = time.Now()
72+
}
6673

67-
respCC := ParseCacheControl(http.Header(resp.Header), &requestTime, &responseTime, t.sharedCache, ttlSeconds)
68-
if slices.Contains(t.cacheableStatus, resp.StatusCode) && respCC.Cacheable() && respDB != nil {
69-
newRespDB, err := db.HttpToResponse(resp)
70-
if err == nil {
71-
newRespDB.TableName = respDB.TableName
72-
t.querier.Write(context.Background(), url, newRespDB)
73-
}
74+
if respDB != nil {
75+
respCC := ParseCacheControl(http.Header(respDB.Header), &respDB.RequestTime, &respDB.ResponseTime, t.sharedCache, ttlSeconds)
76+
if !respCC.Expired() {
77+
return &http.Response{
78+
StatusCode: respDB.Status,
79+
Body: respDB.Body,
80+
Header: http.Header(respDB.Header),
81+
}, nil
7482
}
75-
76-
return resp, err
7783
}
7884

79-
respCC := ParseCacheControl(http.Header(respDB.Header), &respDB.RequestTime, &respDB.ResponseTime, t.sharedCache, ttlSeconds)
80-
if respCC.Expired() {
81-
return t.base.RoundTrip(req)
85+
if resp == nil {
86+
requestTime = time.Now()
87+
resp, err = t.base.RoundTrip(req)
88+
if err != nil {
89+
return nil, err
90+
}
91+
responseTime = time.Now()
8292
}
93+
respCC := ParseCacheControl(http.Header(resp.Header), &requestTime, &responseTime, t.sharedCache, ttlSeconds)
94+
if slices.Contains(t.cacheableStatus, resp.StatusCode) && respCC.Cacheable() {
95+
newRespDB, err := db.HttpToResponse(resp)
96+
if err == nil {
97+
if respDB != nil {
98+
newRespDB.TableName = respDB.TableName
99+
}
100+
t.querier.Write(context.Background(), url, newRespDB)
101+
}
83102

84-
return &http.Response{
85-
StatusCode: respDB.Status,
86-
Body: respDB.Body,
87-
Header: http.Header(respDB.Header),
88-
}, nil
103+
}
89104

105+
return resp, err
90106
}
91107

92108
func (t *readWriteRFC9111Transport) Close() error {

http/rw_transport.go

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,27 +44,42 @@ func (t *readWriteTransport) RoundTrip(req *http.Request) (*http.Response, error
4444
return t.base.RoundTrip(req)
4545
}
4646

47+
var (
48+
resp *http.Response
49+
err error
50+
)
4751
url := req.URL.String()
4852
respDB, err := t.querier.FindByURL(req.Context(), url)
49-
if err != nil || (t.ttl > 0 && time.Since(respDB.ResponseTime) > t.ttl) ||
50-
(len(t.cacheableStatus) > 0 && !slices.Contains(t.cacheableStatus, respDB.Status)) {
51-
resp, err := t.base.RoundTrip(req)
52-
if err == nil && respDB != nil {
53-
newRespDB, err := db.HttpToResponse(resp)
54-
if err == nil {
55-
newRespDB.TableName = respDB.TableName
56-
t.querier.Write(context.Background(), url, newRespDB)
57-
}
53+
if err != nil {
54+
resp, err = t.base.RoundTrip(req)
55+
if err != nil {
56+
return nil, err
5857
}
59-
return resp, err
6058
}
6159

62-
return &http.Response{
63-
StatusCode: respDB.Status,
64-
Body: respDB.Body,
65-
Header: http.Header(respDB.Header),
66-
}, nil
60+
if respDB != nil && !((t.ttl > 0 && time.Since(respDB.ResponseTime) > t.ttl) ||
61+
(len(t.cacheableStatus) > 0 && !slices.Contains(t.cacheableStatus, respDB.Status))) {
62+
return &http.Response{
63+
StatusCode: respDB.Status,
64+
Body: respDB.Body,
65+
Header: http.Header(respDB.Header),
66+
}, nil
67+
}
68+
if resp == nil {
69+
resp, err = t.base.RoundTrip(req)
70+
if err != nil {
71+
return nil, err
72+
}
73+
}
74+
newRespDB, err := db.HttpToResponse(resp)
75+
if err == nil {
76+
if respDB != nil {
77+
newRespDB.TableName = respDB.TableName
78+
}
79+
t.querier.Write(context.Background(), url, newRespDB)
80+
}
6781

82+
return resp, err
6883
}
6984

7085
func (t *readWriteTransport) Close() error {

0 commit comments

Comments
 (0)