diff --git a/weed/admin/handlers/file_browser_handlers.go b/weed/admin/handlers/file_browser_handlers.go index ee214ae40..8c3e1ac9f 100644 --- a/weed/admin/handlers/file_browser_handlers.go +++ b/weed/admin/handlers/file_browser_handlers.go @@ -48,6 +48,15 @@ func NewFileBrowserHandlers(adminServer *dash.AdminServer) *FileBrowserHandlers } } +// newClientWithTimeout creates a temporary http.Client with the specified timeout, +// reusing the TLS transport from the shared httpClient. +func (h *FileBrowserHandlers) newClientWithTimeout(timeout time.Duration) http.Client { + return http.Client{ + Transport: h.httpClient.Client.Transport, + Timeout: timeout, + } +} + // ShowFileBrowser renders the file browser page func (h *FileBrowserHandlers) ShowFileBrowser(c *gin.Context) { // Get path from query parameter, default to root @@ -384,11 +393,8 @@ func (h *FileBrowserHandlers) uploadFileToFiler(filePath string, fileHeader *mul // lgtm[go/ssrf] // Safe: filerAddress validated by validateFilerAddress() to match configured filer // Safe: cleanFilePath validated and cleaned by validateAndCleanFilePath() to prevent path traversal - clientWithTimeout := http.Client{ - Transport: h.httpClient.Client.Transport, - Timeout: 60 * time.Second, - } - resp, err := clientWithTimeout.Do(req) + client := h.newClientWithTimeout(60 * time.Second) + resp, err := client.Do(req) if err != nil { return fmt.Errorf("failed to upload file: %w", err) } @@ -496,11 +502,8 @@ func (h *FileBrowserHandlers) fetchFileContent(filePath string, timeout time.Dur // lgtm[go/ssrf] // Safe: filerAddress validated by validateFilerAddress() to match configured filer // Safe: cleanFilePath validated and cleaned by validateAndCleanFilePath() to prevent path traversal - clientWithTimeout := http.Client{ - Transport: h.httpClient.Client.Transport, - Timeout: timeout, - } - resp, err := clientWithTimeout.Get(fileURL) + client := h.newClientWithTimeout(timeout) + resp, err := client.Get(fileURL) if err != nil { return "", fmt.Errorf("failed to fetch file from filer: %w", err) } @@ -560,11 +563,8 @@ func (h *FileBrowserHandlers) DownloadFile(c *gin.Context) { // lgtm[go/ssrf] // Safe: filerAddress validated by validateFilerAddress() to match configured filer // Safe: cleanFilePath validated and cleaned by validateAndCleanFilePath() to prevent path traversal - clientWithTimeout := http.Client{ - Transport: h.httpClient.Client.Transport, - Timeout: 5 * time.Minute, // Longer timeout for large file downloads - } - resp, err := clientWithTimeout.Get(downloadURL) + client := h.newClientWithTimeout(5 * time.Minute) // Longer timeout for large file downloads + resp, err := client.Get(downloadURL) if err != nil { c.JSON(http.StatusBadGateway, gin.H{"error": "Failed to fetch file from filer: " + err.Error()}) return @@ -572,7 +572,8 @@ func (h *FileBrowserHandlers) DownloadFile(c *gin.Context) { defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - c.JSON(resp.StatusCode, gin.H{"error": fmt.Sprintf("Filer returned status %d", resp.StatusCode)}) + body, _ := io.ReadAll(resp.Body) + c.JSON(resp.StatusCode, gin.H{"error": fmt.Sprintf("Filer returned status %d: %s", resp.StatusCode, string(body))}) return } @@ -993,11 +994,8 @@ func (h *FileBrowserHandlers) isLikelyTextFile(filePath string, maxCheckSize int // lgtm[go/ssrf] // Safe: filerAddress validated by validateFilerAddress() to match configured filer // Safe: cleanFilePath validated and cleaned by validateAndCleanFilePath() to prevent path traversal - clientWithTimeout := http.Client{ - Transport: h.httpClient.Client.Transport, - Timeout: 10 * time.Second, - } - resp, err := clientWithTimeout.Get(fileURL) + client := h.newClientWithTimeout(10 * time.Second) + resp, err := client.Get(fileURL) if err != nil || resp.StatusCode != http.StatusOK { return false }