133 lines
2.8 KiB
Go
133 lines
2.8 KiB
Go
//go:build linux || freebsd
|
|||
|
|
// +build linux freebsd
|
||
|
|
|
||
|
|
package protocol
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"net/url"
|
||
|
|
"sort"
|
||
|
|
"strings"
|
||
|
|
"sync"
|
||
|
|
|
||
|
|
"codeberg.org/petrbalvin/goget/internal/core"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Registry manages all registered protocols
|
||
|
|
type Registry struct {
|
||
|
|
mu sync.RWMutex
|
||
|
|
protocols map[string]core.Protocol
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewRegistry creates a new registry
|
||
|
|
func NewRegistry() *Registry {
|
||
|
|
return &Registry{
|
||
|
|
protocols: make(map[string]core.Protocol),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Register registers a protocol under its primary scheme
|
||
|
|
func (r *Registry) Register(p core.Protocol) error {
|
||
|
|
r.mu.Lock()
|
||
|
|
defer r.mu.Unlock()
|
||
|
|
|
||
|
|
scheme := p.Scheme()
|
||
|
|
if scheme == "" {
|
||
|
|
return fmt.Errorf("protocol scheme cannot be empty")
|
||
|
|
}
|
||
|
|
|
||
|
|
if _, exists := r.protocols[scheme]; exists {
|
||
|
|
return fmt.Errorf("protocol already registered: %s", scheme)
|
||
|
|
}
|
||
|
|
|
||
|
|
r.protocols[scheme] = p
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// normalizeScheme normalizes a scheme for lookup (https -> http, webdavs -> webdav, etc.)
|
||
|
|
func normalizeScheme(scheme string) string {
|
||
|
|
scheme = strings.ToLower(scheme)
|
||
|
|
switch scheme {
|
||
|
|
case "https":
|
||
|
|
return "http"
|
||
|
|
case "webdavs":
|
||
|
|
return "webdav"
|
||
|
|
case "ftps":
|
||
|
|
return "ftp"
|
||
|
|
default:
|
||
|
|
return scheme
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get gets a protocol for a given URL with scheme normalization
|
||
|
|
func (r *Registry) Get(u *url.URL) (core.Protocol, error) {
|
||
|
|
r.mu.RLock()
|
||
|
|
defer r.mu.RUnlock()
|
||
|
|
|
||
|
|
if u == nil {
|
||
|
|
return nil, fmt.Errorf("url cannot be nil")
|
||
|
|
}
|
||
|
|
|
||
|
|
scheme := normalizeScheme(u.Scheme)
|
||
|
|
p, exists := r.protocols[scheme]
|
||
|
|
if !exists {
|
||
|
|
return nil, fmt.Errorf("unsupported protocol: %s", u.Scheme)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Additional validation via CanHandle
|
||
|
|
if !p.CanHandle(u) {
|
||
|
|
return nil, fmt.Errorf("protocol %s cannot handle url: %s", scheme, u.String())
|
||
|
|
}
|
||
|
|
|
||
|
|
return p, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// List returns a list of all registered protocols
|
||
|
|
func (r *Registry) List() []string {
|
||
|
|
r.mu.RLock()
|
||
|
|
defer r.mu.RUnlock()
|
||
|
|
|
||
|
|
schemes := make([]string, 0, len(r.protocols))
|
||
|
|
for scheme := range r.protocols {
|
||
|
|
schemes = append(schemes, scheme)
|
||
|
|
}
|
||
|
|
|
||
|
|
sort.Strings(schemes)
|
||
|
|
return schemes
|
||
|
|
}
|
||
|
|
|
||
|
|
// Supports determines whether a protocol is supported (with normalization)
|
||
|
|
func (r *Registry) Supports(scheme string) bool {
|
||
|
|
r.mu.RLock()
|
||
|
|
defer r.mu.RUnlock()
|
||
|
|
|
||
|
|
_, exists := r.protocols[normalizeScheme(scheme)]
|
||
|
|
return exists
|
||
|
|
}
|
||
|
|
|
||
|
|
// Capabilities returns the capabilities of all protocols
|
||
|
|
func (r *Registry) Capabilities() map[string][]string {
|
||
|
|
r.mu.RLock()
|
||
|
|
defer r.mu.RUnlock()
|
||
|
|
|
||
|
|
caps := make(map[string][]string)
|
||
|
|
for scheme, p := range r.protocols {
|
||
|
|
caps[scheme] = p.Capabilities()
|
||
|
|
}
|
||
|
|
|
||
|
|
return caps
|
||
|
|
}
|
||
|
|
|
||
|
|
// GlobalRegistry is the global registry instance
|
||
|
|
var GlobalRegistry = NewRegistry()
|
||
|
|
|
||
|
|
// Register is a helper for registering in the global registry
|
||
|
|
func Register(p core.Protocol) error {
|
||
|
|
return GlobalRegistry.Register(p)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get is a helper for getting a protocol from the global registry
|
||
|
|
func Get(u *url.URL) (core.Protocol, error) {
|
||
|
|
return GlobalRegistry.Get(u)
|
||
|
|
}
|