Files

72 lines
2.1 KiB
Go

package sdkserver
import (
"net/http"
"os"
"path/filepath"
"regexp"
"strings"
"gac-hkrpg-go/internal/common"
)
// Route name is part of this server's HTTP interface, not a machine URL.
const luaOverlayRoute = "/gacy-lua-overlay"
var luaBundleName = regexp.MustCompile(`^(?:M_LuaV|LuaV_[0-9a-f]{32}|[0-9a-f]{32})\.bytes$`)
// Dispatch outside ServeMux: a subtree registration conflicts with the
// product-name wildcard used by the existing login endpoints in newer Go.
func withLuaOverlay(next http.Handler, cfg common.LuaOverlayConfig) http.Handler {
if !cfg.Enabled {
return next
}
overlay := luaOverlayHandler(cfg)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, luaOverlayRoute+"/") {
overlay(w, r)
return
}
next.ServeHTTP(w, r)
})
}
// Serve only the Lua updater's known flat namespace. Stage files win, then the
// local hotfix and shipping files; no filesystem path or remote URL is derived
// from untrusted input. The original caches remain untouched.
func luaOverlayHandler(cfg common.LuaOverlayConfig) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet && r.Method != http.MethodHead {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
path := strings.TrimPrefix(r.URL.Path, luaOverlayRoute+"/")
path = strings.TrimPrefix(path, "client/Windows/")
if !luaBundleName.MatchString(path) {
http.NotFound(w, r)
return
}
for _, root := range []string{cfg.StageDir, cfg.HotfixDir, cfg.StreamingDir} {
file, err := os.Open(filepath.Join(root, path))
if os.IsNotExist(err) {
continue
}
if err != nil {
http.Error(w, "Lua overlay read failed", http.StatusInternalServerError)
return
}
info, err := file.Stat()
if err != nil || !info.Mode().IsRegular() {
file.Close()
http.Error(w, "Lua overlay file invalid", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/octet-stream")
http.ServeContent(w, r, path, info.ModTime(), file)
file.Close()
return
}
http.NotFound(w, r)
}
}