Signed-off-by: Suleiman Dibirov <idsulik@gmail.com>
pull/31405/head
Suleiman Dibirov 12 months ago
parent e2cbf044cc
commit e248d394d8

@ -68,9 +68,16 @@ func NewExtractor(source string) (Extractor, error) {
return extractor, nil return extractor, nil
} }
} }
if strings.HasPrefix(source, "http") && isGzipArchiveFromURL(source) { if strings.HasPrefix(source, "http") {
isGzip, err := isGzipArchiveFromURL(source)
if err != nil {
return nil, err
}
if isGzip {
return &TarGzExtractor{}, nil return &TarGzExtractor{}, nil
} }
}
return nil, fmt.Errorf("no extractor implemented yet for %s", source) return nil, fmt.Errorf("no extractor implemented yet for %s", source)
} }

@ -15,7 +15,13 @@ limitations under the License.
package installer package installer
import "net/http" import (
"context"
"fmt"
"io"
"net/http"
"time"
)
// isGzipArchive checks if data represents a gzip archive by checking the magic bytes // isGzipArchive checks if data represents a gzip archive by checking the magic bytes
func isGzipArchive(data []byte) bool { func isGzipArchive(data []byte) bool {
@ -23,11 +29,15 @@ func isGzipArchive(data []byte) bool {
} }
// isGzipArchiveFromURL checks if a URL points to a gzip archive by reading the magic bytes // isGzipArchiveFromURL checks if a URL points to a gzip archive by reading the magic bytes
func isGzipArchiveFromURL(url string) bool { func isGzipArchiveFromURL(url string) (bool, error) {
// Use a short timeout context to avoid hanging on slow servers
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// Make a GET request to read the first few bytes // Make a GET request to read the first few bytes
req, err := http.NewRequest(http.MethodGet, url, nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil { if err != nil {
return false return false, err
} }
// Request only the first 512 bytes to check magic bytes // Request only the first 512 bytes to check magic bytes
@ -35,16 +45,22 @@ func isGzipArchiveFromURL(url string) bool {
resp, err := http.DefaultClient.Do(req) resp, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
return false return false, err
} }
defer resp.Body.Close() defer resp.Body.Close()
// Read the first few bytes // Check for valid status codes early
// 206 = Partial Content (range supported)
// 200 = OK (range not supported, full content returned)
if resp.StatusCode != http.StatusPartialContent && resp.StatusCode != http.StatusOK {
return false, fmt.Errorf("unexpected status code %d when checking gzip archive at %s", resp.StatusCode, url)
}
// Read exactly 2 bytes for gzip magic number check
buf := make([]byte, 2) buf := make([]byte, 2)
n, err := resp.Body.Read(buf) if _, err := io.ReadAtLeast(resp.Body, buf, len(buf)); err != nil {
if err != nil || n < 2 { return false, fmt.Errorf("failed to read magic bytes from %s: %w", url, err)
return false
} }
return isGzipArchive(buf) return isGzipArchive(buf), nil
} }

@ -196,11 +196,19 @@ func isRemoteHTTPArchive(source string) bool {
contentType := res.Header.Get("content-type") contentType := res.Header.Get("content-type")
foundSuffix, ok := mediaTypeToExtension(contentType) foundSuffix, ok := mediaTypeToExtension(contentType)
if !ok { if !ok {
if contentType == "application/octet-stream" && isGzipArchiveFromURL(source) { if contentType == "application/octet-stream" {
isGzip, err := isGzipArchiveFromURL(source)
if err != nil {
slog.Debug("isGzipArchiveFromURL", slog.Any("error", err))
return false
}
if isGzip {
// For generic binary content, try to detect the actual file type // For generic binary content, try to detect the actual file type
// by reading the first few bytes (magic bytes) // by reading the first few bytes (magic bytes)
return true return true
} }
}
return false return false
} }

Loading…
Cancel
Save