Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 56bb1ddc12 |
@@ -2,6 +2,7 @@
|
|||||||
package steamcache
|
package steamcache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
@@ -24,6 +25,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"bytes"
|
||||||
|
|
||||||
"github.com/docker/go-units"
|
"github.com/docker/go-units"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
@@ -146,6 +149,19 @@ func verifyResponseHash(resp *http.Response, bodyData []byte, expectedHash strin
|
|||||||
return strings.EqualFold(actualHash, expectedHash)
|
return strings.EqualFold(actualHash, expectedHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hopByHopHeaders = map[string]struct{}{
|
||||||
|
"Connection": {},
|
||||||
|
"Keep-Alive": {},
|
||||||
|
"Proxy-Authenticate": {},
|
||||||
|
"Proxy-Authorization": {},
|
||||||
|
"TE": {},
|
||||||
|
"Trailer": {},
|
||||||
|
"Transfer-Encoding": {},
|
||||||
|
"Upgrade": {},
|
||||||
|
"Date": {},
|
||||||
|
"Server": {},
|
||||||
|
}
|
||||||
|
|
||||||
type SteamCache struct {
|
type SteamCache struct {
|
||||||
address string
|
address string
|
||||||
upstream string
|
upstream string
|
||||||
@@ -357,26 +373,42 @@ func (sc *SteamCache) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
w.Header().Add("X-LanCache-Processed-By", "SteamCache2") // SteamPrefill uses this header to determine if the request was processed by the cache maybe steam uses it too
|
w.Header().Add("X-LanCache-Processed-By", "SteamCache2") // SteamPrefill uses this header to determine if the request was processed by the cache maybe steam uses it too
|
||||||
|
|
||||||
reader, err := sc.vfs.Open(cacheKey)
|
cachePath := cacheKey // You may want to add a .http or .cache extension for clarity
|
||||||
|
|
||||||
|
// Try to serve from cache
|
||||||
|
file, err := sc.vfs.Open(cachePath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
defer reader.Close()
|
defer file.Close()
|
||||||
w.Header().Add("X-LanCache-Status", "HIT")
|
buf := bufio.NewReader(file)
|
||||||
|
resp, err := http.ReadResponse(buf, nil)
|
||||||
io.Copy(w, reader)
|
if err == nil {
|
||||||
|
// Remove hop-by-hop and server-specific headers
|
||||||
|
for k, vv := range resp.Header {
|
||||||
|
if _, skip := hopByHopHeaders[http.CanonicalHeaderKey(k)]; skip {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, v := range vv {
|
||||||
|
w.Header().Add(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add our own headers
|
||||||
|
w.Header().Set("X-LanCache-Status", "HIT")
|
||||||
|
w.Header().Set("X-LanCache-Processed-By", "SteamCache2")
|
||||||
|
w.WriteHeader(resp.StatusCode)
|
||||||
|
io.Copy(w, resp.Body)
|
||||||
|
resp.Body.Close()
|
||||||
logger.Logger.Info().
|
logger.Logger.Info().
|
||||||
Str("key", cacheKey).
|
Str("key", cacheKey).
|
||||||
Str("host", r.Host).
|
Str("host", r.Host).
|
||||||
Str("status", "HIT").
|
Str("status", "HIT").
|
||||||
Dur("duration", time.Since(tstart)).
|
Dur("duration", time.Since(tstart)).
|
||||||
Msg("request")
|
Msg("request")
|
||||||
|
|
||||||
requestsTotal.WithLabelValues(r.Method, "200").Inc()
|
requestsTotal.WithLabelValues(r.Method, "200").Inc()
|
||||||
cacheStatusTotal.WithLabelValues("HIT").Inc()
|
cacheStatusTotal.WithLabelValues("HIT").Inc()
|
||||||
responseTime.WithLabelValues("HIT").Observe(time.Since(tstart).Seconds())
|
responseTime.WithLabelValues("HIT").Observe(time.Since(tstart).Seconds())
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var req *http.Request
|
var req *http.Request
|
||||||
if sc.upstream != "" { // if an upstream server is configured, proxy the request to the upstream server
|
if sc.upstream != "" { // if an upstream server is configured, proxy the request to the upstream server
|
||||||
@@ -448,8 +480,6 @@ func (sc *SteamCache) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
size := resp.ContentLength
|
|
||||||
|
|
||||||
// Read the entire response body into memory for hash verification
|
// Read the entire response body into memory for hash verification
|
||||||
bodyData, err := io.ReadAll(resp.Body)
|
bodyData, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -500,15 +530,28 @@ func (sc *SteamCache) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write to response (always serve the file)
|
// Write to response (always serve the file)
|
||||||
w.Header().Add("X-LanCache-Status", "MISS")
|
// Remove hop-by-hop and server-specific headers
|
||||||
|
for k, vv := range resp.Header {
|
||||||
|
if _, skip := hopByHopHeaders[http.CanonicalHeaderKey(k)]; skip {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, v := range vv {
|
||||||
|
w.Header().Add(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add our own headers
|
||||||
|
w.Header().Set("X-LanCache-Status", "MISS")
|
||||||
|
w.Header().Set("X-LanCache-Processed-By", "SteamCache2")
|
||||||
w.Write(bodyData)
|
w.Write(bodyData)
|
||||||
|
|
||||||
// Only cache the file if hash verification passed (or no hash was present)
|
// Only cache the file if hash verification passed (or no hash was present)
|
||||||
if hashVerified {
|
if hashVerified {
|
||||||
writer, _ := sc.vfs.Create(cacheKey, size)
|
writer, _ := sc.vfs.Create(cachePath, int64(0)) // size is not known in advance
|
||||||
if writer != nil {
|
if writer != nil {
|
||||||
defer writer.Close()
|
defer writer.Close()
|
||||||
writer.Write(bodyData)
|
// Write the full HTTP response to cache
|
||||||
|
resp.Body = io.NopCloser(bytes.NewReader(bodyData)) // Reset body for writing
|
||||||
|
resp.Write(writer)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.Logger.Warn().
|
logger.Logger.Warn().
|
||||||
|
|||||||
Reference in New Issue
Block a user