mirror of
https://gh-proxy.org/https://github.com/klinsmaya/CLIProxyAPI.git
synced 2026-11-08 01:16:47 +08:00
refactor(plugin): remove timeout handling from plugin installation logic
This commit is contained in:
@@ -21,10 +21,6 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// defaultPluginStoreInstallTimeout bounds plugin store install downloads so
|
||||
// a stalled registry, release, or asset request does not hold the management
|
||||
// request forever.
|
||||
defaultPluginStoreInstallTimeout = 5 * time.Minute
|
||||
// pluginReleaseCacheTTL bounds how long a resolved latest release version is
|
||||
// reused before the GitHub API is queried again.
|
||||
pluginReleaseCacheTTL = 10 * time.Minute
|
||||
@@ -143,28 +139,15 @@ func (h *Handler) InstallPluginFromStore(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (h *Handler) installPluginFromStore(c *gin.Context, goos, goarch string) {
|
||||
h.installPluginFromStoreWithTimeout(c, goos, goarch, defaultPluginStoreInstallTimeout)
|
||||
}
|
||||
|
||||
func (h *Handler) installPluginFromStoreWithTimeout(c *gin.Context, goos, goarch string, timeout time.Duration) {
|
||||
id, okID := pluginIDFromRequest(c)
|
||||
if !okID {
|
||||
return
|
||||
}
|
||||
installCtx := c.Request.Context()
|
||||
if timeout > 0 {
|
||||
var cancel context.CancelFunc
|
||||
installCtx, cancel = context.WithTimeout(installCtx, timeout)
|
||||
defer cancel()
|
||||
}
|
||||
pluginsEnabled, pluginsDir, proxyURL, _, host := h.pluginStoreSnapshot()
|
||||
client := h.newPluginStoreClient(proxyURL)
|
||||
registry, errRegistry := client.FetchRegistry(installCtx)
|
||||
if errRegistry != nil {
|
||||
if pluginStoreRequestTimedOut(installCtx, errRegistry) {
|
||||
c.JSON(http.StatusGatewayTimeout, gin.H{"error": "plugin_store_timeout", "message": "plugin store request timed out"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusBadGateway, gin.H{"error": "plugin_store_registry_failed", "message": errRegistry.Error()})
|
||||
return
|
||||
}
|
||||
@@ -214,10 +197,6 @@ func (h *Handler) installPluginFromStoreWithTimeout(c *gin.Context, goos, goarch
|
||||
})
|
||||
return
|
||||
}
|
||||
if pluginStoreRequestTimedOut(installCtx, errInstall) {
|
||||
c.JSON(http.StatusGatewayTimeout, gin.H{"error": "plugin_install_timeout", "message": "plugin install timed out"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusBadGateway, gin.H{"error": "plugin_install_failed", "message": errInstall.Error()})
|
||||
return
|
||||
}
|
||||
@@ -272,13 +251,6 @@ func (h *Handler) installPluginFromStoreWithTimeout(c *gin.Context, goos, goarch
|
||||
})
|
||||
}
|
||||
|
||||
func pluginStoreRequestTimedOut(ctx context.Context, err error) bool {
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
return true
|
||||
}
|
||||
return ctx != nil && errors.Is(ctx.Err(), context.DeadlineExceeded)
|
||||
}
|
||||
|
||||
// enablePluginConfigLocked sets plugins.configs.<id>.enabled to true while preserving
|
||||
// the rest of the plugin's raw configuration. Callers must hold h.mu.
|
||||
func (h *Handler) enablePluginConfigLocked(id string) error {
|
||||
|
||||
@@ -17,7 +17,6 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
@@ -318,62 +317,6 @@ func TestInstallPluginFromStoreWritesFileAndEnablesConfig(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstallPluginFromStoreTimesOutDownloadingAsset(t *testing.T) {
|
||||
t.Parallel()
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
pluginsDir := t.TempDir()
|
||||
archiveName := "sample-provider_0.1.0_" + runtime.GOOS + "_" + runtime.GOARCH + ".zip"
|
||||
archiveURL := "https://downloads.example/" + archiveName
|
||||
h := &Handler{
|
||||
cfg: &config.Config{
|
||||
Plugins: config.PluginsConfig{
|
||||
Enabled: false,
|
||||
Dir: pluginsDir,
|
||||
},
|
||||
},
|
||||
configFilePath: writeTestConfigFile(t),
|
||||
pluginStoreRegistryURL: "https://registry.example/registry.json",
|
||||
pluginStoreHTTPClient: blockingPluginStoreHTTPClient{
|
||||
blockURL: archiveURL,
|
||||
responses: fakePluginStoreHTTPClient{
|
||||
"https://registry.example/registry.json": registryJSON(t),
|
||||
"https://api.github.com/repos/author-name/cliproxy-sample-provider-plugin/releases/latest": []byte(`{
|
||||
"tag_name": "v0.1.0",
|
||||
"assets": [
|
||||
{"name": "` + archiveName + `", "browser_download_url": "` + archiveURL + `"},
|
||||
{"name": "checksums.txt", "browser_download_url": "https://downloads.example/checksums.txt"}
|
||||
]
|
||||
}`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(rec)
|
||||
c.Params = gin.Params{{Key: "id", Value: "sample-provider"}}
|
||||
c.Request = httptest.NewRequest(http.MethodPost, "/v0/management/plugin-store/sample-provider/install", nil)
|
||||
|
||||
h.installPluginFromStoreWithTimeout(c, runtime.GOOS, runtime.GOARCH, 20*time.Millisecond)
|
||||
|
||||
if rec.Code != http.StatusGatewayTimeout {
|
||||
t.Fatalf("status = %d, want %d; body=%s", rec.Code, http.StatusGatewayTimeout, rec.Body.String())
|
||||
}
|
||||
var body struct {
|
||||
Error string `json:"error"`
|
||||
}
|
||||
if errDecode := json.Unmarshal(rec.Body.Bytes(), &body); errDecode != nil {
|
||||
t.Fatalf("Unmarshal() error = %v; body=%s", errDecode, rec.Body.String())
|
||||
}
|
||||
if body.Error != "plugin_install_timeout" {
|
||||
t.Fatalf("error = %q, want plugin_install_timeout", body.Error)
|
||||
}
|
||||
targetPath := filepath.Join(pluginsDir, runtime.GOOS, runtime.GOARCH, "sample-provider"+managementPluginExtension(runtime.GOOS))
|
||||
if _, errStat := os.Stat(targetPath); !os.IsNotExist(errStat) {
|
||||
t.Fatalf("target stat error = %v, want not exist", errStat)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstallPluginFromStoreOverwritesFilePreservesConfigAndReloads(t *testing.T) {
|
||||
t.Parallel()
|
||||
gin.SetMode(gin.TestMode)
|
||||
@@ -518,19 +461,6 @@ func (c fakePluginStoreHTTPClient) Do(req *http.Request) (*http.Response, error)
|
||||
}, nil
|
||||
}
|
||||
|
||||
type blockingPluginStoreHTTPClient struct {
|
||||
responses fakePluginStoreHTTPClient
|
||||
blockURL string
|
||||
}
|
||||
|
||||
func (c blockingPluginStoreHTTPClient) Do(req *http.Request) (*http.Response, error) {
|
||||
if req.URL.String() == c.blockURL {
|
||||
<-req.Context().Done()
|
||||
return nil, req.Context().Err()
|
||||
}
|
||||
return c.responses.Do(req)
|
||||
}
|
||||
|
||||
type countingPluginStoreHTTPClient struct {
|
||||
responses fakePluginStoreHTTPClient
|
||||
mu sync.Mutex
|
||||
|
||||
Reference in New Issue
Block a user