feat: initial goget release — modern IPv6-first download utility

This commit is contained in:
2026-06-26 21:21:58 +02:00
commit 53db81e1f7
147 changed files with 45931 additions and 0 deletions
+150
View File
@@ -0,0 +1,150 @@
//go:build linux || freebsd
// +build linux freebsd
package compression
import (
"io"
"strings"
"sync"
)
// Decompressor defines the interface for decompression
type Decompressor interface {
Name() string
CanHandle(contentEncoding string) bool
CanHandleFile(filename string) bool
// Reader returns an io.ReadCloser (not just an io.Reader) so
// callers can release the decompressor's internal state — decoder
// tables, dictionaries, etc. — when finished. Forgetting to call
// Close on a long-lived mirror scan would let decoder state
// accumulate without bound.
Reader(r io.Reader) (io.ReadCloser, error)
}
// Compressor defines the interface for compression
type Compressor interface {
Name() string
Writer(w io.Writer) (io.WriteCloser, error)
}
// BaseDecompressor is the base implementation
type BaseDecompressor struct {
name string
contentEncodings []string
fileExtensions []string
}
func NewBaseDecompressor(name string, contentEncodings, fileExtensions []string) *BaseDecompressor {
return &BaseDecompressor{
name: name,
contentEncodings: contentEncodings,
fileExtensions: fileExtensions,
}
}
func (bd *BaseDecompressor) Name() string { return bd.name }
func (bd *BaseDecompressor) Extensions() []string { return bd.fileExtensions }
func (bd *BaseDecompressor) CanHandle(contentEncoding string) bool {
for _, e := range bd.contentEncodings {
if strings.EqualFold(e, contentEncoding) {
return true
}
}
return false
}
func (bd *BaseDecompressor) CanHandleFile(filename string) bool {
filename = strings.ToLower(filename)
for _, e := range bd.fileExtensions {
if strings.HasSuffix(filename, e) {
return true
}
}
return false
}
// Registry for decompressors
type registry struct {
mu sync.RWMutex
items map[string]Decompressor
}
var globalRegistry = &registry{items: make(map[string]Decompressor)}
func Register(d Decompressor) {
globalRegistry.mu.Lock()
defer globalRegistry.mu.Unlock()
globalRegistry.items[strings.ToLower(d.Name())] = d
}
func Get(name string) (Decompressor, bool) {
globalRegistry.mu.RLock()
defer globalRegistry.mu.RUnlock()
d, ok := globalRegistry.items[strings.ToLower(name)]
return d, ok
}
func GetByFilename(filename string) (Decompressor, bool) {
format := DetectCompressionByFilename(filename)
if format == "" || format == "identity" {
return nil, false
}
return Get(format)
}
// GetCompressionFromHeader detects compression from the Content-Encoding header
func GetCompressionFromHeader(contentEncoding string) string {
if contentEncoding == "" {
return "identity"
}
encodings := ParseContentEncoding(contentEncoding)
return GetEffectiveEncoding(encodings)
}
func ParseContentEncoding(header string) []string {
if header == "" {
return []string{"identity"}
}
parts := strings.Split(header, ",")
result := make([]string, 0, len(parts))
for _, p := range parts {
p = strings.TrimSpace(p)
if p != "" {
result = append(result, strings.ToLower(p))
}
}
if len(result) == 0 {
return []string{"identity"}
}
return result
}
func GetEffectiveEncoding(encodings []string) string {
for _, e := range encodings {
if e != "identity" {
return e
}
}
return "identity"
}
func DetectCompressionByFilename(filename string) string {
filename = strings.ToLower(filename)
switch {
case strings.HasSuffix(filename, ".gz"):
return "gzip"
case strings.HasSuffix(filename, ".zlib"), strings.HasSuffix(filename, ".zz"):
return "zlib"
case strings.HasSuffix(filename, ".deflate"), strings.HasSuffix(filename, ".fl"):
return "flate"
case strings.HasSuffix(filename, ".bz2"), strings.HasSuffix(filename, ".tbz"), strings.HasSuffix(filename, ".tbz2"):
return "bzip2"
case strings.HasSuffix(filename, ".z"), strings.HasSuffix(filename, ".lzw"):
return "lzw"
}
return "identity"
}
func IsCompressedFile(filename string) bool {
return DetectCompressionByFilename(filename) != "identity"
}