Files
wehub-resource-sync ead81af521
CI / go (push) Waiting to run
CI / build (darwin, macos-14) (push) Waiting to run
CI / build (windows, windows-2025) (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Failing after 0s
chore: import upstream snapshot with attribution
2026-07-13 12:31:13 +08:00

61 lines
1.5 KiB
Go

// Package sshurl parses ssh:// URLs into components for use with the ssh binary.
package sshurl
import (
"fmt"
"net"
"net/url"
)
// Parsed holds the components of an ssh:// URL.
type Parsed struct {
Host string // hostname or user@hostname
Port string // port number, or "" for default
Path string // absolute remote path (starts with /)
}
// SSHArgs returns the ssh command arguments for connecting to this host.
// If a port is specified, -p is included.
func (p Parsed) SSHArgs() []string {
args := []string{"-o", "BatchMode=yes", "-o", "StrictHostKeyChecking=yes", "-o", "ConnectTimeout=5"}
if p.Port != "" {
args = append(args, "-p", p.Port)
}
args = append(args, p.Host)
return args
}
// Parse parses an ssh:// URL into host, port, and path components.
// Accepts formats like ssh://host/path, ssh://user@host/path, ssh://host:2222/path.
func Parse(raw string) (Parsed, error) {
u, err := url.Parse(raw)
if err != nil {
return Parsed{}, fmt.Errorf("invalid ssh URL %q: %w", raw, err)
}
if u.Scheme != "ssh" {
return Parsed{}, fmt.Errorf("expected ssh:// scheme, got %q", u.Scheme)
}
if u.Path == "" {
return Parsed{}, fmt.Errorf("ssh URL missing path: %s", raw)
}
host := u.Hostname()
if u.User != nil {
host = u.User.Username() + "@" + host
}
port := u.Port()
// net/url splits host:port correctly; verify with SplitHostPort for edge cases.
if port == "" && u.Host != host {
if _, p, err := net.SplitHostPort(u.Host); err == nil {
port = p
}
}
return Parsed{
Host: host,
Port: port,
Path: u.Path,
}, nil
}