Skip to content

Commit 6402f80

Browse files
committed
perf(provider): optimize HTTP client connections for Copilot requests
1 parent 5d8a241 commit 6402f80

2 files changed

Lines changed: 267 additions & 70 deletions

File tree

internal/config/token_cache.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package config
2+
3+
import (
4+
"crypto/sha256"
5+
"encoding/hex"
6+
"encoding/json"
7+
"fmt"
8+
"os"
9+
"path/filepath"
10+
"sync"
11+
"time"
12+
)
13+
14+
// CopilotTokenCache holds a cached Copilot API token with expiration metadata.
15+
type CopilotTokenCache struct {
16+
Token string `json:"token"`
17+
ExpiresAt time.Time `json:"expires_at"`
18+
GitHubTokenHash string `json:"github_token_hash"`
19+
}
20+
21+
var (
22+
memoryCache *CopilotTokenCache
23+
memoryCacheMu sync.RWMutex
24+
)
25+
26+
func getCacheFilePath() string {
27+
return filepath.Join(getConfigDir(), ".lazycommit.token.cache")
28+
}
29+
30+
func hashToken(token string) string {
31+
h := sha256.Sum256([]byte(token))
32+
return hex.EncodeToString(h[:])
33+
}
34+
35+
// GetCachedCopilotToken returns a valid cached token for the given GitHub token, or nil if unavailable.
36+
func GetCachedCopilotToken(githubToken string) *CopilotTokenCache {
37+
tokenHash := hashToken(githubToken)
38+
39+
memoryCacheMu.RLock()
40+
if memoryCache != nil &&
41+
memoryCache.GitHubTokenHash == tokenHash &&
42+
isTokenValid(memoryCache) {
43+
cache := *memoryCache
44+
memoryCacheMu.RUnlock()
45+
return &cache
46+
}
47+
memoryCacheMu.RUnlock()
48+
49+
cache, err := loadCacheFromDisk()
50+
if err != nil {
51+
return nil
52+
}
53+
54+
if cache.GitHubTokenHash != tokenHash || !isTokenValid(cache) {
55+
return nil
56+
}
57+
58+
memoryCacheMu.Lock()
59+
memoryCache = cache
60+
memoryCacheMu.Unlock()
61+
62+
return cache
63+
}
64+
65+
// SaveCopilotTokenCache persists the token to memory and disk.
66+
func SaveCopilotTokenCache(token string, expiresAtUnix int64, githubToken string) error {
67+
cache := &CopilotTokenCache{
68+
Token: token,
69+
ExpiresAt: time.Unix(expiresAtUnix, 0),
70+
GitHubTokenHash: hashToken(githubToken),
71+
}
72+
73+
memoryCacheMu.Lock()
74+
memoryCache = cache
75+
memoryCacheMu.Unlock()
76+
77+
return saveCacheToDisk(cache)
78+
}
79+
80+
// InvalidateCopilotTokenCache clears the cached token from memory and disk.
81+
func InvalidateCopilotTokenCache() {
82+
memoryCacheMu.Lock()
83+
memoryCache = nil
84+
memoryCacheMu.Unlock()
85+
86+
_ = os.Remove(getCacheFilePath())
87+
}
88+
89+
func isTokenValid(cache *CopilotTokenCache) bool {
90+
if cache == nil || cache.Token == "" {
91+
return false
92+
}
93+
expirationBuffer := 60 * time.Second
94+
return time.Now().Add(expirationBuffer).Before(cache.ExpiresAt)
95+
}
96+
97+
func loadCacheFromDisk() (*CopilotTokenCache, error) {
98+
data, err := os.ReadFile(getCacheFilePath())
99+
if err != nil {
100+
return nil, err
101+
}
102+
103+
var cache CopilotTokenCache
104+
if err := json.Unmarshal(data, &cache); err != nil {
105+
return nil, err
106+
}
107+
108+
return &cache, nil
109+
}
110+
111+
func saveCacheToDisk(cache *CopilotTokenCache) error {
112+
data, err := json.Marshal(cache)
113+
if err != nil {
114+
return fmt.Errorf("failed to marshal token cache: %w", err)
115+
}
116+
117+
cacheDir := filepath.Dir(getCacheFilePath())
118+
if err := os.MkdirAll(cacheDir, 0700); err != nil {
119+
return fmt.Errorf("failed to create cache directory: %w", err)
120+
}
121+
122+
if err := os.WriteFile(getCacheFilePath(), data, 0600); err != nil {
123+
return fmt.Errorf("failed to write token cache: %w", err)
124+
}
125+
126+
return nil
127+
}

0 commit comments

Comments
 (0)