feat: use ContentType to control HTML extension

This commit is contained in:
2026-06-29 11:55:11 +02:00
parent 277178776d
commit 6e58502598
4 changed files with 36 additions and 2 deletions
+19 -2
View File
@@ -130,9 +130,12 @@ func runPostDownload(
fmt.Fprintf(os.Stderr, "Warning: failed to close writer: %v\n", cerr) fmt.Fprintf(os.Stderr, "Warning: failed to close writer: %v\n", cerr)
} }
// Adjust extension: append .html to HTML files without extension // Adjust extension: append .html to HTML files without extension.
// When ContentType is available (WebDAV, Gopher), only apply to
// text/html responses. When ContentType is empty (HTTP legacy
// path), preserve the existing wget-compatible behaviour.
if *f.AdjustExt && *f.Output != "" { if *f.AdjustExt && *f.Output != "" {
if filepath.Ext(*f.Output) == "" { if filepath.Ext(*f.Output) == "" && isHTMLContentType(result.ContentType) {
newPath := *f.Output + ".html" newPath := *f.Output + ".html"
if err := os.Rename(*f.Output, newPath); err == nil { if err := os.Rename(*f.Output, newPath); err == nil {
*f.Output = newPath *f.Output = newPath
@@ -289,6 +292,20 @@ func runPostDownload(
return 0 return 0
} }
// isHTMLContentType returns true when ct indicates HTML content.
// An empty string is treated as HTML for backward compatibility with
// protocol handlers that do not populate ContentType (e.g. HTTP).
func isHTMLContentType(ct string) bool {
if ct == "" {
return true
}
// Strip parameters (charset, boundary, …).
if i := strings.IndexByte(ct, ';'); i >= 0 {
ct = ct[:i]
}
return strings.TrimSpace(ct) == "text/html"
}
// splitCommand splits a shell-like command string into tokens respecting double quotes. // splitCommand splits a shell-like command string into tokens respecting double quotes.
// Used to parse --on-complete and --on-complete-url without invoking a shell. // Used to parse --on-complete and --on-complete-url without invoking a shell.
func splitCommand(raw string) []string { func splitCommand(raw string) []string {
+1
View File
@@ -103,6 +103,7 @@ type DownloadResult struct {
Parallel bool Parallel bool
ChunksCount int ChunksCount int
HTTPStatusCode int // HTTP status code from response HTTPStatusCode int // HTTP status code from response
ContentType string // Content-Type from response (if available)
TraceTimings *TraceTimings // HTTP tracing breakdown TraceTimings *TraceTimings // HTTP tracing breakdown
} }
+15
View File
@@ -209,9 +209,22 @@ func (p *Protocol) downloadText(ctx context.Context, reader *bufio.Reader, req *
IPVersion: 4, IPVersion: 4,
Speed: speed, Speed: speed,
OutputPath: req.Output, OutputPath: req.Output,
ContentType: gopherContentType(firstType),
}, nil }, nil
} }
// gopherContentType returns the MIME content type for a gopher item type.
func gopherContentType(t byte) string {
switch t {
case typeHTML:
return "text/html"
case typeTextFile, typeInformation:
return "text/plain"
default:
return "application/octet-stream"
}
}
// downloadDirectory formats a directory listing as readable text. // downloadDirectory formats a directory listing as readable text.
func (p *Protocol) downloadDirectory(reader *bufio.Reader, req *core.DownloadRequest, startTime time.Time) (*core.DownloadResult, error) { func (p *Protocol) downloadDirectory(reader *bufio.Reader, req *core.DownloadRequest, startTime time.Time) (*core.DownloadResult, error) {
var writer io.Writer var writer io.Writer
@@ -261,6 +274,7 @@ func (p *Protocol) downloadDirectory(reader *bufio.Reader, req *core.DownloadReq
IPVersion: 4, IPVersion: 4,
Speed: speed, Speed: speed,
OutputPath: req.Output, OutputPath: req.Output,
ContentType: "text/plain",
}, nil }, nil
} }
@@ -313,6 +327,7 @@ func (p *Protocol) downloadBinary(ctx context.Context, reader *bufio.Reader, req
IPVersion: 4, IPVersion: 4,
Speed: speed, Speed: speed,
OutputPath: req.Output, OutputPath: req.Output,
ContentType: "application/octet-stream",
}, nil }, nil
} }
+1
View File
@@ -680,6 +680,7 @@ func (p *Protocol) downloadFile(ctx context.Context, req *core.DownloadRequest,
OutputPath: req.Output, OutputPath: req.Output,
Resumed: resumed, Resumed: resumed,
ChunksCount: 1, ChunksCount: 1,
ContentType: resp.Header.Get("Content-Type"),
}, nil }, nil
} }